nm-setting.c 66.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 * Copyright 2007 - 2011 Red Hat, Inc.
 * Copyright 2007 - 2008 Novell, Inc.
 */

23
#include "nm-default.h"
24

25
#include "nm-setting.h"
26 27 28

#include <string.h>

29 30
#include "nm-setting-private.h"
#include "nm-utils.h"
31
#include "nm-core-internal.h"
32
#include "nm-utils-private.h"
33
#include "nm-property-compare.h"
34

35 36 37 38 39 40 41 42 43
#include "nm-setting-connection.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
#include "nm-setting-bridge-port.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
#include "nm-setting-vpn.h"

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/**
 * SECTION:nm-setting
 * @short_description: Describes related configuration information
 *
 * Each #NMSetting contains properties that describe configuration that applies
 * to a specific network layer (like IPv4 or IPv6 configuration) or device type
 * (like Ethernet, or Wi-Fi).  A collection of individual settings together
 * make up an #NMConnection. Each property is strongly typed and usually has
 * a number of allowed values.  See each #NMSetting subclass for a description
 * of properties and allowed values.
 */

G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)

#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))

typedef struct {
	const char *name;
	GType type;
63
	NMSettingPriority priority;
64 65 66 67 68 69 70 71 72 73 74 75 76
} SettingInfo;

typedef struct {
	const SettingInfo *info;
} NMSettingPrivate;

enum {
	PROP_0,
	PROP_NAME,

	PROP_LAST
};

77
/*****************************************************************************/
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

static GHashTable *registered_settings = NULL;
static GHashTable *registered_settings_by_type = NULL;

static gboolean
_nm_gtype_equal (gconstpointer v1, gconstpointer v2)
{
	return *((const GType *) v1) == *((const GType *) v2);
}
static guint
_nm_gtype_hash (gconstpointer v)
{
	return *((const GType *) v);
}

93
static void
94 95 96
_ensure_registered (void)
{
	if (G_UNLIKELY (registered_settings == NULL)) {
97
		registered_settings = g_hash_table_new (nm_str_hash, g_str_equal);
98 99 100 101
		registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal);
	}
}

102 103 104 105 106 107
static void __attribute__((constructor))
_ensure_registered_constructor (void)
{
	_ensure_registered ();
}

108 109 110 111 112 113 114 115 116
#define _ensure_setting_info(self, priv) \
	G_STMT_START { \
		NMSettingPrivate *_priv_esi = (priv); \
		if (G_UNLIKELY (!_priv_esi->info)) { \
			_priv_esi->info = _nm_setting_lookup_setting_by_type (G_OBJECT_TYPE (self)); \
			g_assert (_priv_esi->info); \
		} \
	} G_STMT_END

117
/*****************************************************************************/
118 119

/*
120
 * _nm_register_setting_impl:
121 122
 * @name: the name of the #NMSetting object to register
 * @type: the #GType of the #NMSetting
123
 * @priority: the sort priority of the setting, see #NMSettingPriority
124
 *
125
 * INTERNAL ONLY: registers a setting's internal properties with libnm.
126 127
 */
void
128
_nm_register_setting_impl (const char *name,
129 130
                           GType type,
                           NMSettingPriority priority)
131 132 133
{
	SettingInfo *info;

134 135 136
	nm_assert (name && *name);
	nm_assert (!NM_IN_SET (type, G_TYPE_INVALID, G_TYPE_NONE));
	nm_assert (priority != NM_SETTING_PRIORITY_INVALID);
137 138 139

	_ensure_registered ();

140 141
	nm_assert (!g_hash_table_lookup (registered_settings, name));
	nm_assert (!g_hash_table_lookup (registered_settings_by_type, &type));
142

143 144
	nm_assert (   priority != NM_SETTING_PRIORITY_CONNECTION
	           || nm_streq (name, NM_SETTING_CONNECTION_SETTING_NAME));
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

	info = g_slice_new0 (SettingInfo);
	info->type = type;
	info->priority = priority;
	info->name = name;
	g_hash_table_insert (registered_settings, (void *) info->name, info);
	g_hash_table_insert (registered_settings_by_type, &info->type, info);
}

static const SettingInfo *
_nm_setting_lookup_setting_by_type (GType type)
{
	_ensure_registered ();
	return g_hash_table_lookup (registered_settings_by_type, &type);
}

161
static NMSettingPriority
162 163 164 165 166 167 168 169 170 171
_get_setting_type_priority (GType type)
{
	const SettingInfo *info;

	g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32);

	info = _nm_setting_lookup_setting_by_type (type);
	return info->priority;
}

172
NMSettingPriority
173 174 175 176 177 178 179 180 181 182
_nm_setting_get_setting_priority (NMSetting *setting)
{
	NMSettingPrivate *priv;

	g_return_val_if_fail (NM_IS_SETTING (setting), G_MAXUINT32);
	priv = NM_SETTING_GET_PRIVATE (setting);
	_ensure_setting_info (setting, priv);
	return priv->info->priority;
}

183
NMSettingPriority
184
_nm_setting_type_get_base_type_priority (GType type)
185
{
186
	NMSettingPriority priority;
187

188 189 190 191 192
	/* Historical oddity: PPPoE is a base-type even though it's not
	 * priority 1.  It needs to be sorted *after* lower-level stuff like
	 * Wi-Fi security or 802.1x for secrets, but it's still allowed as a
	 * base type.
	 */
193
	priority = _get_setting_type_priority (type);
194 195 196 197
	if (   NM_IN_SET (priority,
	                  NM_SETTING_PRIORITY_HW_BASE,
	                  NM_SETTING_PRIORITY_HW_NON_BASE)
	    || type == NM_TYPE_SETTING_PPPOE)
198 199
		return priority;
	else
200
		return NM_SETTING_PRIORITY_INVALID;
201 202
}

203
NMSettingPriority
204
_nm_setting_get_base_type_priority (NMSetting *setting)
205
{
206
	return _nm_setting_type_get_base_type_priority (G_OBJECT_TYPE (setting));
207 208
}

209 210 211 212 213 214
/**
 * nm_setting_lookup_type:
 * @name: a setting name
 *
 * Returns the #GType of the setting's class for a given setting name.
 *
215 216
 * Returns: the #GType of the setting's class, or %G_TYPE_INVALID if
 *   @name is not recognized.
217
 **/
218
GType
219
nm_setting_lookup_type (const char *name)
220
{
221
	const SettingInfo *info;
222

223
	g_return_val_if_fail (name, G_TYPE_INVALID);
224 225 226 227 228 229 230 231 232 233

	_ensure_registered ();

	info = g_hash_table_lookup (registered_settings, name);
	return info ? info->type : G_TYPE_INVALID;
}

gint
_nm_setting_compare_priority (gconstpointer a, gconstpointer b)
{
234
	NMSettingPriority prio_a, prio_b;
235

236 237
	prio_a = _nm_setting_get_setting_priority ((NMSetting *) a);
	prio_b = _nm_setting_get_setting_priority ((NMSetting *) b);
238 239 240 241 242 243 244 245

	if (prio_a < prio_b)
		return -1;
	else if (prio_a == prio_b)
		return 0;
	return 1;
}

246
/*****************************************************************************/
247

248 249 250 251 252 253 254 255 256 257 258 259
gboolean
_nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_type)
{
	const char *port_type = NULL;
	gboolean found = TRUE;

	if (!slave_type)
		found = FALSE;
	else if (!strcmp (slave_type, NM_SETTING_BOND_SETTING_NAME))
		;
	else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
		port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
260 261
	else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
		port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
262 263
	else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
		port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
264 265 266 267 268 269 270 271 272 273
	else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
		port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
	else
		found = FALSE;

	if (out_port_type)
		*out_port_type = port_type;
	return found;
}

274
/*****************************************************************************/
275

276 277 278
typedef struct {
	const char *name;
	GParamSpec *param_spec;
279
	const GVariantType *dbus_type;
280

281
	NMSettingPropertyGetFunc get_func;
282
	NMSettingPropertySynthFunc synth_func;
283
	NMSettingPropertySetFunc set_func;
284
	NMSettingPropertyNotSetFunc not_set_func;
285

286 287
	NMSettingPropertyTransformToFunc to_dbus;
	NMSettingPropertyTransformFromFunc from_dbus;
288 289
} NMSettingProperty;

290 291
static NM_CACHED_QUARK_FCN ("nm-setting-property-overrides", setting_property_overrides_quark)
static NM_CACHED_QUARK_FCN ("nm-setting-properties", setting_properties_quark)
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

static NMSettingProperty *
find_property (GArray *properties, const char *name)
{
	NMSettingProperty *property;
	int i;

	if (!properties)
		return NULL;

	for (i = 0; i < properties->len; i++) {
		property = &g_array_index (properties, NMSettingProperty, i);
		if (strcmp (name, property->name) == 0)
			return property;
	}

	return NULL;
}

static void
add_property_override (NMSettingClass *setting_class,
                       const char *property_name,
314
                       GParamSpec *param_spec,
315
                       const GVariantType *dbus_type,
316
                       NMSettingPropertyGetFunc get_func,
317
                       NMSettingPropertySynthFunc synth_func,
318
                       NMSettingPropertySetFunc set_func,
319
                       NMSettingPropertyNotSetFunc not_set_func,
320 321
                       NMSettingPropertyTransformToFunc to_dbus,
                       NMSettingPropertyTransformFromFunc from_dbus)
322 323 324 325 326
{
	GType setting_type = G_TYPE_FROM_CLASS (setting_class);
	GArray *overrides;
	NMSettingProperty override;

327
	g_return_if_fail (g_type_get_qdata (setting_type, setting_properties_quark ()) == NULL);
328 329 330

	memset (&override, 0, sizeof (override));
	override.name = property_name;
331
	override.param_spec = param_spec;
332 333
	override.dbus_type = dbus_type;
	override.get_func = get_func;
334
	override.synth_func = synth_func;
335
	override.set_func = set_func;
336
	override.not_set_func = not_set_func;
337 338
	override.to_dbus = to_dbus;
	override.from_dbus = from_dbus;
339

340
	overrides = g_type_get_qdata (setting_type, setting_property_overrides_quark ());
341 342
	if (!overrides) {
		overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
343
		g_type_set_qdata (setting_type, setting_property_overrides_quark (), overrides);
344 345 346 347 348 349 350 351 352 353
	}
	g_return_if_fail (find_property (overrides, property_name) == NULL);

	g_array_append_val (overrides, override);
}

/**
 * _nm_setting_class_add_dbus_only_property:
 * @setting_class: the setting class
 * @property_name: the name of the property to override
354
 * @dbus_type: the type of the property (in its D-Bus representation)
355
 * @synth_func: (allow-none): function to call to synthesize a value for the property
356 357 358 359 360 361
 * @set_func: (allow-none): function to call to set the value of the property
 *
 * Registers a property named @property_name, which will be used in the D-Bus
 * serialization of objects of @setting_class, but which does not correspond to
 * a #GObject property.
 *
362 363 364 365
 * When serializing a setting to D-Bus, @synth_func will be called to synthesize
 * a value for the property. (If it returns %NULL, no value will be added to the
 * serialization. If @synth_func is %NULL, the property will always be omitted
 * in the serialization.)
366 367
 *
 * When deserializing a D-Bus representation into a setting, if @property_name
368 369
 * is present, then @set_func will be called to set it. (If @set_func is %NULL
 * then the property will be ignored when deserializing.)
370 371 372 373
 */
void
_nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
                                          const char *property_name,
374
                                          const GVariantType *dbus_type,
375
                                          NMSettingPropertySynthFunc synth_func,
376 377 378 379 380 381 382 383 384
                                          NMSettingPropertySetFunc set_func)
{
	g_return_if_fail (NM_IS_SETTING_CLASS (setting_class));
	g_return_if_fail (property_name != NULL);

	/* Must not match any GObject property. */
	g_return_if_fail (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), property_name));

	add_property_override (setting_class,
385
	                       property_name, NULL, dbus_type,
386
	                       NULL, synth_func, set_func, NULL,
387
	                       NULL, NULL);
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
}

/**
 * _nm_setting_class_override_property:
 * @setting_class: the setting class
 * @property_name: the name of the property to override
 * @dbus_type: the type of the property (in its D-Bus representation)
 * @get_func: (allow-none): function to call to get the value of the property
 * @set_func: (allow-none): function to call to set the value of the property
 * @not_set_func: (allow-none): function to call to indicate the property was not set
 *
 * Overrides the D-Bus representation of the #GObject property named
 * @property_name on @setting_class.
 *
 * When serializing a setting to D-Bus, if @get_func is non-%NULL, then it will
403 404 405 406 407
 * be called to get the property's value. If it returns a #GVariant, the
 * property will be added to the hash, and if it returns %NULL, the property
 * will be omitted. (If @get_func is %NULL, the property will be read normally
 * with g_object_get_property(), and added to the hash if it is not the default
 * value.)
408 409
 *
 * When deserializing a D-Bus representation into a setting, if @property_name
410 411
 * is present, then @set_func will be called to set it. (If @set_func is %NULL
 * then the property will be set normally with g_object_set_property().)
412 413 414 415 416 417 418 419 420
 *
 * If @not_set_func is non-%NULL, then it will be called when deserializing a
 * representation that does NOT contain @property_name. This can be used, eg, if
 * a new property needs to be initialized from some older deprecated property
 * when it is not present.
 */
void
_nm_setting_class_override_property (NMSettingClass *setting_class,
                                     const char *property_name,
421
                                     const GVariantType *dbus_type,
422 423 424 425 426 427 428 429 430 431 432
                                     NMSettingPropertyGetFunc get_func,
                                     NMSettingPropertySetFunc set_func,
                                     NMSettingPropertyNotSetFunc not_set_func)
{
	GParamSpec *param_spec;

	param_spec = g_object_class_find_property (G_OBJECT_CLASS (setting_class), property_name);
	g_return_if_fail (param_spec != NULL);

	add_property_override (setting_class,
	                       property_name, param_spec, dbus_type,
433
	                       get_func, NULL, set_func, not_set_func,
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
	                       NULL, NULL);
}

/**
 * _nm_setting_class_transform_property:
 * @setting_class: the setting class
 * @property: the name of the property to transform
 * @dbus_type: the type of the property (in its D-Bus representation)
 * @to_dbus: function to convert from object to D-Bus format
 * @from_dbus: function to convert from D-Bus to object format
 *
 * Indicates that @property on @setting_class does not have the same format as
 * its corresponding D-Bus representation, and so must be transformed when
 * serializing/deserializing.
 *
 * The transformation will also be used by nm_setting_compare(), meaning that
 * the underlying object property does not need to be of a type that
 * nm_property_compare() recognizes, as long as it recognizes @dbus_type.
 */
void
_nm_setting_class_transform_property (NMSettingClass *setting_class,
                                      const char *property,
456 457 458
                                      const GVariantType *dbus_type,
                                      NMSettingPropertyTransformToFunc to_dbus,
                                      NMSettingPropertyTransformFromFunc from_dbus)
459 460 461 462 463 464 465 466
{
	GParamSpec *param_spec;

	param_spec = g_object_class_find_property (G_OBJECT_CLASS (setting_class), property);
	g_return_if_fail (param_spec != NULL);

	add_property_override (setting_class,
	                       property, param_spec, dbus_type,
467
	                       NULL, NULL, NULL, NULL,
468
	                       to_dbus, from_dbus);
469 470
}

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
gboolean
_nm_setting_use_legacy_property (NMSetting *setting,
                                 GVariant *connection_dict,
                                 const char *legacy_property,
                                 const char *new_property)
{
	GVariant *setting_dict, *value;

	setting_dict = g_variant_lookup_value (connection_dict, nm_setting_get_name (NM_SETTING (setting)), NM_VARIANT_TYPE_SETTING);
	g_return_val_if_fail (setting_dict != NULL, FALSE);

	/* If the new property isn't set, we have to use the legacy property. */
	value = g_variant_lookup_value (setting_dict, new_property, NULL);
	if (!value) {
		g_variant_unref (setting_dict);
		return TRUE;
	}
	g_variant_unref (value);

	/* Otherwise, clients always prefer new properties sent from the daemon. */
	if (!_nm_utils_is_manager_process) {
		g_variant_unref (setting_dict);
		return FALSE;
	}

	/* The daemon prefers the legacy property if it exists. */
	value = g_variant_lookup_value (setting_dict, legacy_property, NULL);
	g_variant_unref (setting_dict);

	if (value) {
		g_variant_unref (value);
		return TRUE;
	} else
		return FALSE;
}

507 508 509 510 511 512 513 514 515
static GArray *
nm_setting_class_ensure_properties (NMSettingClass *setting_class)
{
	GType type = G_TYPE_FROM_CLASS (setting_class), otype;
	NMSettingProperty property, *override;
	GArray *overrides, *type_overrides, *properties;
	GParamSpec **property_specs;
	guint n_property_specs, i;

516
	properties = g_type_get_qdata (type, setting_properties_quark ());
517 518 519 520 521 522
	if (properties)
		return properties;

	/* Build overrides array from @setting_class and its superclasses */
	overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
	for (otype = type; otype != G_TYPE_OBJECT; otype = g_type_parent (otype)) {
523
		type_overrides = g_type_get_qdata (otype, setting_property_overrides_quark ());
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
		if (type_overrides)
			g_array_append_vals (overrides, (NMSettingProperty *)type_overrides->data, type_overrides->len);
	}

	/* Build the properties array from the GParamSpecs, obeying overrides */
	properties = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));

	property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
	                                                 &n_property_specs);
	for (i = 0; i < n_property_specs; i++) {
		override = find_property (overrides, property_specs[i]->name);
		if (override)
			property = *override;
		else {
			memset (&property, 0, sizeof (property));
			property.name = property_specs[i]->name;
			property.param_spec = property_specs[i];
		}
		g_array_append_val (properties, property);
	}
	g_free (property_specs);

	/* Add any remaining overrides not corresponding to GObject properties */
	for (i = 0; i < overrides->len; i++) {
		override = &g_array_index (overrides, NMSettingProperty, i);
		if (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), override->name))
			g_array_append_val (properties, *override);
	}
	g_array_unref (overrides);

554
	g_type_set_qdata (type, setting_properties_quark (), properties);
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
	return properties;
}

static const NMSettingProperty *
nm_setting_class_get_properties (NMSettingClass *setting_class, guint *n_properties)
{
	GArray *properties;

	properties = nm_setting_class_ensure_properties (setting_class);

	*n_properties = properties->len;
	return (NMSettingProperty *) properties->data;
}

static const NMSettingProperty *
nm_setting_class_find_property (NMSettingClass *setting_class, const char *property_name)
{
	GArray *properties;

	properties = nm_setting_class_ensure_properties (setting_class);
	return find_property (properties, property_name);
}

578
/*****************************************************************************/
579

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
static const GVariantType *
variant_type_for_gtype (GType type)
{
	if (type == G_TYPE_BOOLEAN)
		return G_VARIANT_TYPE_BOOLEAN;
	else if (type == G_TYPE_UCHAR)
		return G_VARIANT_TYPE_BYTE;
	else if (type == G_TYPE_INT)
		return G_VARIANT_TYPE_INT32;
	else if (type == G_TYPE_UINT)
		return G_VARIANT_TYPE_UINT32;
	else if (type == G_TYPE_INT64)
		return G_VARIANT_TYPE_INT64;
	else if (type == G_TYPE_UINT64)
		return G_VARIANT_TYPE_UINT64;
	else if (type == G_TYPE_STRING)
		return G_VARIANT_TYPE_STRING;
	else if (type == G_TYPE_DOUBLE)
		return G_VARIANT_TYPE_DOUBLE;
	else if (type == G_TYPE_STRV)
		return G_VARIANT_TYPE_STRING_ARRAY;
601 602 603 604 605 606
	else if (type == G_TYPE_BYTES)
		return G_VARIANT_TYPE_BYTESTRING;
	else if (g_type_is_a (type, G_TYPE_ENUM))
		return G_VARIANT_TYPE_INT32;
	else if (g_type_is_a (type, G_TYPE_FLAGS))
		return G_VARIANT_TYPE_UINT32;
607 608 609 610 611 612 613 614 615 616 617 618
	else
		g_assert_not_reached ();
}

static GVariant *
get_property_for_dbus (NMSetting *setting,
                       const NMSettingProperty *property,
                       gboolean ignore_default)
{
	GValue prop_value = { 0, };
	GVariant *dbus_value;

619 620 621 622
	if (property->get_func)
		return property->get_func (setting, property->name);
	else
		g_return_val_if_fail (property->param_spec != NULL, NULL);
623 624 625 626 627 628 629 630 631 632 633 634 635

	g_value_init (&prop_value, property->param_spec->value_type);
	g_object_get_property (G_OBJECT (setting), property->param_spec->name, &prop_value);

	if (ignore_default && g_param_value_defaults (property->param_spec, &prop_value)) {
		g_value_unset (&prop_value);
		return NULL;
	}

	if (property->to_dbus)
		dbus_value = property->to_dbus (&prop_value);
	else if (property->dbus_type)
		dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, property->dbus_type);
636 637 638 639
	else if (g_type_is_a (prop_value.g_type, G_TYPE_ENUM))
		dbus_value = g_variant_new_int32 (g_value_get_enum (&prop_value));
	else if (g_type_is_a (prop_value.g_type, G_TYPE_FLAGS))
		dbus_value = g_variant_new_uint32 (g_value_get_flags (&prop_value));
640 641
	else if (prop_value.g_type == G_TYPE_BYTES)
		dbus_value = _nm_utils_bytes_to_dbus (&prop_value);
642 643 644 645 646 647 648
	else
		dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, variant_type_for_gtype (prop_value.g_type));
	g_value_unset (&prop_value);

	return dbus_value;
}

649 650 651 652
static gboolean
set_property_from_dbus (const NMSettingProperty *property,
                        GVariant *src_value,
                        GValue *dst_value)
653
{
654
	g_return_val_if_fail (property->param_spec != NULL, FALSE);
655

656 657 658 659
	if (property->from_dbus) {
		if (!g_variant_type_equal (g_variant_get_type (src_value), property->dbus_type))
			return FALSE;

660
		property->from_dbus (src_value, dst_value);
661 662 663 664
	} else if (dst_value->g_type == G_TYPE_BYTES) {
		if (!g_variant_is_of_type (src_value, G_VARIANT_TYPE_BYTESTRING))
			return FALSE;

665
		_nm_utils_bytes_from_dbus (src_value, dst_value);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
	} else {
		GValue tmp = G_VALUE_INIT;

		g_dbus_gvariant_to_gvalue (src_value, &tmp);
		if (G_VALUE_TYPE (&tmp) == G_VALUE_TYPE (dst_value))
			*dst_value = tmp;
		else {
			gboolean success;

			success = g_value_transform (&tmp, dst_value);
			g_value_unset (&tmp);
			if (!success)
				return FALSE;
		}
	}

	return TRUE;
683 684
}

685

686
/**
687
 * _nm_setting_to_dbus:
688
 * @setting: the #NMSetting
689
 * @connection: the #NMConnection containing @setting
690
 * @flags: hash flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
691
 *
692 693 694
 * Converts the #NMSetting into a #GVariant of type #NM_VARIANT_TYPE_SETTING
 * mapping each setting property name to a value describing that property,
 * suitable for marshalling over D-Bus or serializing.
695
 *
696 697
 * Returns: (transfer none): a new floating #GVariant describing the setting's
 * properties
698
 **/
699
GVariant *
700
_nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
701
{
702 703
	GVariantBuilder builder;
	GVariant *dbus_value;
704 705
	const NMSettingProperty *properties;
	guint n_properties, i;
706 707 708

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);

709
	properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
710

711
	g_variant_builder_init (&builder, NM_VARIANT_TYPE_SETTING);
712

713 714 715 716
	for (i = 0; i < n_properties; i++) {
		const NMSettingProperty *property = &properties[i];
		GParamSpec *prop_spec = property->param_spec;

717 718
		if (!prop_spec && !property->synth_func) {
			/* D-Bus-only property with no synth_func, so we skip it. */
719 720
			continue;
		}
721

722
		if (prop_spec && !(prop_spec->flags & G_PARAM_WRITABLE))
723 724
			continue;

725 726 727 728
		if (   prop_spec && (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
		    && !_nm_utils_is_manager_process)
			continue;

729
		if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
730
		    && (prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
731 732
			continue;

733
		if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
734
		    && !(prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
735 736
			continue;

737 738
		if (property->synth_func)
			dbus_value = property->synth_func (setting, connection, property->name);
739
		else
740
			dbus_value = get_property_for_dbus (setting, property, TRUE);
741 742 743
		if (dbus_value) {
			/* Allow dbus_value to be either floating or not. */
			g_variant_take_ref (dbus_value);
744

745 746 747
			g_variant_builder_add (&builder, "{sv}", property->name, dbus_value);
			g_variant_unref (dbus_value);
		}
748 749
	}

750
	return g_variant_builder_end (&builder);
751 752 753
}

/**
754
 * _nm_setting_new_from_dbus:
755
 * @setting_type: the #NMSetting type which the hash contains properties for
756 757 758 759
 * @setting_dict: the #GVariant containing an %NM_VARIANT_TYPE_SETTING dictionary
 *   mapping property names to values
 * @connection_dict: the #GVariant containing an %NM_VARIANT_TYPE_CONNECTION
 *   dictionary mapping setting names to dictionaries.
760
 * @parse_flags: flags to determine behavior during parsing.
761
 * @error: location to store error, or %NULL
762 763
 *
 * Creates a new #NMSetting object and populates that object with the properties
764 765 766 767 768
 * contained in @setting_dict, using each key as the property to set, and each
 * value as the value to set that property to.  Setting properties are strongly
 * typed, thus the #GVariantType of the dict value must be correct.  See the
 * documentation on each #NMSetting object subclass for the correct property
 * names and value types.
769 770
 *
 * Returns: a new #NMSetting object populated with the properties from the
771
 * hash table, or %NULL if @setting_hash could not be deserialized.
772 773
 **/
NMSetting *
774
_nm_setting_new_from_dbus (GType setting_type,
775 776
                           GVariant *setting_dict,
                           GVariant *connection_dict,
777
                           NMSettingParseFlags parse_flags,
778
                           GError **error)
779
{
780 781
	gs_unref_object NMSetting *setting = NULL;
	gs_unref_hashtable GHashTable *keys = NULL;
782
	const NMSettingProperty *properties;
783
	guint i, n_properties;
784 785

	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
786 787
	g_return_val_if_fail (g_variant_is_of_type (setting_dict, NM_VARIANT_TYPE_SETTING), NULL);

788 789 790
	nm_assert (!NM_FLAGS_ANY (parse_flags, ~NM_SETTING_PARSE_FLAGS_ALL));
	nm_assert (!NM_FLAGS_ALL (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_BEST_EFFORT));

791 792 793 794 795
	/* connection_dict is not technically optional, but some tests in test-general
	 * don't bother with it in cases where they know it's not needed.
	 */
	if (connection_dict)
		g_return_val_if_fail (g_variant_is_of_type (connection_dict, NM_VARIANT_TYPE_CONNECTION), NULL);
796

797 798 799 800 801
	/* Build the setting object from the properties we know about; we assume
	 * that any propreties in @setting_dict that we don't know about can
	 * either be ignored or else has a backward-compatibility equivalent
	 * that we do know about.
	 */
802
	setting = (NMSetting *) g_object_new (setting_type, NULL);
803

804 805 806 807 808
	if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
		GVariantIter iter;
		GVariant *entry, *entry_key;
		char *key;

809
		keys = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
810 811 812 813 814 815 816 817

		g_variant_iter_init (&iter, setting_dict);
		while ((entry = g_variant_iter_next_value (&iter))) {
			entry_key = g_variant_get_child_value (entry, 0);
			key = g_strdup (g_variant_get_string (entry_key, NULL));
			g_variant_unref (entry_key);
			g_variant_unref (entry);

Lubomir Rintel's avatar
Lubomir Rintel committed
818
			if (!g_hash_table_add (keys, key)) {
819 820 821 822 823 824 825 826
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
				             _("duplicate property"));
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
				return NULL;
			}
		}
	}

827
	properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
828 829
	for (i = 0; i < n_properties; i++) {
		const NMSettingProperty *property = &properties[i];
830 831
		gs_unref_variant GVariant *value = NULL;
		gs_free_error GError *local = NULL;
832 833 834 835 836

		if (property->param_spec && !(property->param_spec->flags & G_PARAM_WRITABLE))
			continue;

		value = g_variant_lookup_value (setting_dict, property->name, NULL);
837

838 839 840
		if (value && keys)
			g_hash_table_remove (keys, property->name);

841
		if (value && property->set_func) {
842

843
			if (!g_variant_type_equal (g_variant_get_type (value), property->dbus_type)) {
844 845 846
				/* for backward behavior, fail unless best-effort is chosen. */
				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
					continue;
847 848 849 850
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can't set property of type '%s' from value of type '%s'"),
				             property->dbus_type ?
				                 g_variant_type_peek_string (property->dbus_type) :
Jiří Klimeš's avatar
Jiří Klimeš committed
851 852
				                 property->param_spec ?
				                     g_type_name (property->param_spec->value_type) : "(unknown)",
853 854 855 856 857
				             g_variant_get_type_string (value));
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}

858 859 860 861 862 863 864 865 866 867 868 869 870 871
			if (!property->set_func (setting,
			                         connection_dict,
			                         property->name,
			                         value,
			                         parse_flags,
			                         &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("failed to set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
872
		} else if (!value && property->not_set_func) {
873 874 875 876 877 878 879 880 881 882 883 884 885
			if (!property->not_set_func (setting,
			                             connection_dict,
			                             property->name,
			                             parse_flags,
			                             &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("failed to set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
886
		} else if (value && property->param_spec) {
887
			nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
888 889

			g_value_init (&object_value, property->param_spec->value_type);
890 891 892 893 894 895 896 897 898 899 900 901 902 903
			if (!set_property_from_dbus (property, value, &object_value)) {
				/* for backward behavior, fail unless best-effort is chosen. */
				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can't set property of type '%s' from value of type '%s'"),
				             property->dbus_type ?
				                 g_variant_type_peek_string (property->dbus_type) :
				                 property->param_spec ?
				                     g_type_name (property->param_spec->value_type) : "(unknown)",
				             g_variant_get_type_string (value));
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
904

905 906 907 908 909 910 911 912 913
			if (!nm_g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value, &local)) {
				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
					continue;
				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
				             _("can not set property: %s"),
				             local->message);
				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
				return NULL;
			}
914
		}
915
	}
916

917 918 919 920 921 922 923 924 925 926 927 928
	if (   NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)
	    && g_hash_table_size (keys) > 0) {
		GHashTableIter iter;
		const char *key;

		g_hash_table_iter_init (&iter, keys);
		if (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
			g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
			             _("unknown property"));
			g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
			return NULL;
		}
929
	}
930

931
	return g_steal_pointer (&setting);
932 933
}

934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
/**
 * nm_setting_get_dbus_property_type:
 * @setting: an #NMSetting
 * @property_name: the property of @setting to get the type of
 *
 * Gets the D-Bus marshalling type of a property. @property_name is a D-Bus
 * property name, which may not necessarily be a #GObject property.
 *
 * Returns: the D-Bus marshalling type of @property on @setting.
 */
const GVariantType *
nm_setting_get_dbus_property_type (NMSetting *setting,
                                   const char *property_name)
{
	const NMSettingProperty *property;

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
	g_return_val_if_fail (property_name != NULL, NULL);

	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), property_name);
	g_return_val_if_fail (property != NULL, NULL);

	if (property->dbus_type)
		return property->dbus_type;
	else
		return variant_type_for_gtype (property->param_spec->value_type);
}

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
gboolean
_nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value)
{
	GParamSpec *prop_spec;

	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
	g_return_val_if_fail (property_name, FALSE);
	g_return_val_if_fail (value, FALSE);

	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);

	if (!prop_spec) {
		g_value_unset (value);
		return FALSE;
	}

	g_value_init (value, prop_spec->value_type);
	g_object_get_property (G_OBJECT (setting), property_name, value);
	return TRUE;
}

983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
static void
duplicate_setting (NMSetting *setting,
                   const char *name,
                   const GValue *value,
                   GParamFlags flags,
                   gpointer user_data)
{
	if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE)
		g_object_set_property (G_OBJECT (user_data), name, value);
}

/**
 * nm_setting_duplicate:
 * @setting: the #NMSetting to duplicate
 *
 * Duplicates a #NMSetting.
 *
 * Returns: (transfer full): a new #NMSetting containing the same properties and values as the
 * source #NMSetting
 **/
NMSetting *
nm_setting_duplicate (NMSetting *setting)
{
	GObject *dup;

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);

	dup = g_object_new (G_OBJECT_TYPE (setting), NULL);

	g_object_freeze_notify (dup);
	nm_setting_enumerate_values (setting, duplicate_setting, dup);
	g_object_thaw_notify (dup);

	return NM_SETTING (dup);
}

/**
 * nm_setting_get_name:
 * @setting: the #NMSetting
 *
 * Returns the type name of the #NMSetting object
 *
 * Returns: a string containing the type name of the #NMSetting object,
 * like 'ppp' or 'wireless' or 'wired'.
 **/
const char *
nm_setting_get_name (NMSetting *setting)
{
	NMSettingPrivate *priv;

	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
	priv = NM_SETTING_GET_PRIVATE (setting);
	_ensure_setting_info (setting, priv);
	return priv->info->name;
}

/**
 * nm_setting_verify:
 * @setting: the #NMSetting to verify
1042 1043
 * @connection: (allow-none): the #NMConnection that @setting came from, or
 *   %NULL if @setting is being verified in isolation.
1044 1045 1046
 * @error: location to store error, or %NULL
 *
 * Validates the setting.  Each setting's properties have allowed values, and
1047
 * some are dependent on other values (hence the need for @connection).  The
1048 1049 1050 1051 1052 1053
 * returned #GError contains information about which property of the setting
 * failed validation, and in what way that property failed validation.
 *
 * Returns: %TRUE if the setting is valid, %FALSE if it is not
 **/
gboolean
1054
nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
1055
{
1056
	NMSettingVerifyResult result = _nm_setting_verify (setting, connection, error);
1057 1058 1059 1060 1061 1062 1063 1064

	if (result == NM_SETTING_VERIFY_NORMALIZABLE)
		g_clear_error (error);

	return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
}

NMSettingVerifyResult
1065
_nm_setting_verify (NMSetting *setting, NMConnection *connection, GError **error)
1066 1067
{
	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_VERIFY_ERROR);
1068
	g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR);
1069 1070 1071
	g_return_val_if_fail (!error || *error == NULL, NM_SETTING_VERIFY_ERROR);

	if (NM_SETTING_GET_CLASS (setting)->verify)
1072
		return NM_SETTING_GET_CLASS (setting)->verify (setting, connection, error);
1073 1074 1075 1076

	return NM_SETTING_VERIFY_SUCCESS;
}

1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
/**
 * nm_setting_verify_secrets:
 * @setting: the #NMSetting to verify secrets in
 * @connection: (allow-none): the #NMConnection that @setting came from, or
 *   %NULL if @setting is being verified in isolation.
 * @error: location to store error, or %NULL
 *
 * Verifies the secrets in the setting.
 * The returned #GError contains information about which secret of the setting
 * failed validation, and in what way that secret failed validation.
 * The secret validation is done separately from main setting validation, because
 * in some cases connection failure is not desired just for the secrets.
 *
 * Returns: %TRUE if the setting secrets are valid, %FALSE if they are not
 *
 * Since: 1.2
 **/
gboolean
nm_setting_verify_secrets (NMSetting *setting, NMConnection *connection, GError **error)
{
	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_VERIFY_ERROR);
	g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR);
	g_return_val_if_fail (!error || *error == NULL, NM_SETTING_VERIFY_ERROR);

	if (NM_SETTING_GET_CLASS (setting)->verify_secrets)
		return NM_SETTING_GET_CLASS (setting)->verify_secrets (setting, connection, error);

	return NM_SETTING_VERIFY_SUCCESS;
}

gboolean
_nm_setting_verify_secret_string (const char *str,
                                  const char *setting_name,
                                  const char *property,
                                  GError **error)
{
	if (str && !*str) {
		g_set_error_literal (error,
		                     NM_CONNECTION_ERROR,
		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
		                     _("property is empty"));
		g_prefix_error (error, "%s.%s: ", setting_name, property);
		return FALSE;
	}
	return TRUE;
}

1124 1125 1126 1127 1128 1129
static gboolean
compare_property (NMSetting *setting,
                  NMSetting *other,
                  const GParamSpec *prop_spec,
                  NMSettingCompareFlags flags)
{
1130
	const NMSettingProperty *property;
1131
	GVariant *value1, *value2;
1132
	int cmp;
1133 1134 1135 1136 1137 1138

	/* Handle compare flags */
	if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
		NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;

1139 1140 1141 1142 1143 1144
		g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);

		if (!nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL))
			g_return_val_if_reached (FALSE);
		if (!nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL))
			g_return_val_if_reached (FALSE);
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161

		/* If the secret flags aren't the same the settings aren't the same */
		if (a_secret_flags != b_secret_flags)
			return FALSE;

		/* Check for various secret flags that might cause us to ignore comparing
		 * this property.
		 */
		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
			return TRUE;

		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
			return TRUE;
	}

1162 1163 1164
	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), prop_spec->name);
	g_return_val_if_fail (property != NULL, FALSE);

1165 1166
	value1 = get_property_for_dbus (setting, property, TRUE);
	value2 = get_property_for_dbus (other, property, TRUE);
1167

1168
	cmp = nm_property_compare (value1, value2);
1169

1170 1171 1172 1173
	if (value1)
		g_variant_unref (value1);
	if (value2)
		g_variant_unref (value2);
1174

1175
	return cmp == 0;
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
}

/**
 * nm_setting_compare:
 * @a: a #NMSetting
 * @b: a second #NMSetting to compare with the first
 * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
 *
 * Compares two #NMSetting objects for similarity, with comparison behavior
 * modified by a set of flags.  See the documentation for #NMSettingCompareFlags
 * for a description of each flag's behavior.
 *
 * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
 **/
gboolean
nm_setting_compare (NMSetting *a,
                    NMSetting *b,
                    NMSettingCompareFlags flags)
{
	GParamSpec **property_specs;
	guint n_property_specs;
	gint same = TRUE;
	guint i;

	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
	g_return_val_if_fail (NM_IS_SETTING (b), FALSE);

	/* First check that both have the same type */
	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
		return FALSE;

	/* And now all properties */
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
	for (i = 0; i < n_property_specs && same; i++) {
		GParamSpec *prop_spec = property_specs[i];

		/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
1213 1214
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY)
		    && !NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
1215 1216
			continue;

1217 1218
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
		    && !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_INFERRABLE))
1219 1220
			continue;

1221 1222 1223 1224
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
			continue;

1225 1226
		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET))
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
			continue;

		same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
	}
	g_free (property_specs);

	return same;
}

static inline gboolean
should_compare_prop (NMSetting *setting,
                     const char *prop_name,
                     NMSettingCompareFlags comp_flags,
                     GParamFlags prop_flags)
{
	/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY)
	    && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
		return FALSE;

	if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
		return FALSE;

1250 1251 1252
	if ((comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY) && !(prop_flags & NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
		return FALSE;

1253 1254 1255 1256 1257 1258
	if (prop_flags & NM_SETTING_PARAM_SECRET) {
		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;

		if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
			return FALSE;

1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
		if (   NM_IS_SETTING_VPN (setting)
		    && g_strcmp0 (prop_name, NM_SETTING_VPN_SECRETS) == 0) {
			/* FIXME: NMSettingVPN:NM_SETTING_VPN_SECRETS has NM_SETTING_PARAM_SECRET.
			 * nm_setting_get_secret_flags() quite possibly fails, but it might succeed if the
			 * setting accidently uses a key "secrets". */
			return TRUE;
		}

		if (!nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL))
			g_return_val_if_reached (FALSE);
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282

		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
		    && (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
			return FALSE;

		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
		    && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
			return FALSE;
	}

	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
	    && NM_IS_SETTING_CONNECTION (setting)
	    && !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
		return FALSE;
1283 1284 1285 1286 1287

	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
	    && NM_IS_SETTING_CONNECTION (setting)
	    && !strcmp (prop_name, NM_SETTING_CONNECTION_TIMESTAMP))
		return FALSE;
1288 1289 1290 1291 1292 1293 1294 1295 1296

	return TRUE;
}

/**
 * nm_setting_diff:
 * @a: a #NMSetting
 * @b: a second #NMSetting to compare with the first
 * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
1297
 * @invert_results: this parameter is used internally by libnm and should
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
 * be set to %FALSE.  If %TRUE inverts the meaning of the #NMSettingDiffResult.
 * @results: (inout) (transfer full) (element-type utf8 guint32): if the
 * settings differ, on return a hash table mapping the differing keys to one or
 * more %NMSettingDiffResult values OR-ed together.  If the settings do not
 * differ, any hash table passed in is unmodified.  If no hash table is passed
 * in and the settings differ, a new one is created and returned.
 *
 * Compares two #NMSetting objects for similarity, with comparison behavior
 * modified by a set of flags.  See the documentation for #NMSettingCompareFlags
 * for a description of each flag's behavior.  If the settings differ, the keys
 * of each setting that differ from the other are added to @results, mapped to
 * one or more #NMSettingDiffResult values.
 *
 * Returns: %TRUE if the settings contain the same values, %FALSE if they do not
 **/
gboolean
nm_setting_diff (NMSetting *a,
                 NMSetting *b,
                 NMSettingCompareFlags flags,
                 gboolean invert_results,
                 GHashTable **results)
{
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;
	NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
	NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
1325 1326
	NMSettingDiffResult a_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
	NMSettingDiffResult b_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
1327
	gboolean results_created = FALSE;
1328
	gboolean compared_any = FALSE;
1329
	gboolean diff_found = FALSE;
1330 1331 1332 1333 1334 1335 1336 1337

	g_return_val_if_fail (results != NULL, FALSE);
	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
	if (b) {
		g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
		g_return_val_if_fail (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b), FALSE);
	}

1338 1339 1340 1341 1342 1343
	if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) ==
	             (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) {
		/* conflicting flags: default to WITH_DEFAULT (clearing NO_DEFAULT). */
		flags &= ~NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT;
	}

1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
	/* If the caller is calling this function in a pattern like this to get
	 * complete diffs:
	 *
	 * nm_setting_diff (A, B, FALSE, &results);
	 * nm_setting_diff (B, A, TRUE, &results);
	 *
	 * and wants us to invert the results so that the second invocation comes
	 * out correctly, do that here.
	 */
	if (invert_results) {
		a_result = NM_SETTING_DIFF_RESULT_IN_B;
		b_result = NM_SETTING_DIFF_RESULT_IN_A;
1356 1357
		a_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
		b_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
1358 1359 1360
	}

	if (*results == NULL) {
1361
		*results = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
1362 1363 1364 1365 1366 1367 1368 1369
		results_created = TRUE;
	}

	/* And now all properties */
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);

	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
1370
		NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN;
1371 1372 1373 1374 1375 1376 1377

		/* Handle compare flags */
		if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags))
			continue;
		if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
			continue;

1378 1379
		compared_any = TRUE;

1380
		if (b) {
1381 1382
			gboolean different;

1383 1384
			different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
			if (different) {
1385
				gboolean a_is_default, b_is_default;
1386 1387 1388 1389
				GValue value = G_VALUE_INIT;

				g_value_init (&value, prop_spec->value_type);
				g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
1390
				a_is_default = g_param_value_defaults (prop_spec, &value);
1391 1392 1393

				g_value_reset (&value);
				g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
1394
				b_is_default = g_param_value_defaults (prop_spec, &value);
1395 1396

				g_value_unset (&value);
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
				if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) {
					if (!a_is_default)
						r |= a_result;
					if (!b_is_default)
						r |= b_result;
				} else {
					r |= a_result | b_result;
					if (a_is_default)
						r |= a_result_default;
					if (b_is_default)
						r |= b_result_default;
				}
1409
			}
1410
		} else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0)
1411
			r = a_result;  /* only in A */
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
		else {
			GValue value = G_VALUE_INIT;

			g_value_init (&value, prop_spec->value_type);
			g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
			if (!g_param_value_defaults (prop_spec, &value))
				r |= a_result;
			else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
				r |= a_result | a_result_default;

			g_value_unset (&value);
		}

		if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
			void *p;
1427

1428
			diff_found = TRUE;
1429 1430 1431 1432 1433
			if (g_hash_table_lookup_extended (*results, prop_spec->name, NULL, &p)) {
				if ((r & GPOINTER_TO_UINT (p)) != r)
					g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r | GPOINTER_TO_UINT (p)));
			} else
				g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r));
1434 1435 1436 1437
		}
	}
	g_free (property_specs);

1438 1439 1440 1441
	if (!compared_any && !b) {
		/* special case: the setting has no properties, and the opposite
		 * setting @b is not given. The settings differ, and we signal that
		 * by returning an empty results hash. */
1442
		diff_found = TRUE;
1443 1444
	}

1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
	if (diff_found) {
		/* if there is a difference, we always return FALSE. It also means, we might
		 * have allocated a new @results hash, and return if to the caller. */
		return FALSE;
	} else {
		if (results_created) {
			/* the allocated hash is unused. Clear it again. */
			g_hash_table_destroy (*results);
			*results = NULL;
		} else {
			/* we found no diff, and return false. However, the input
			 * @result is returned unmodified. */
		}
		return TRUE;
1459 1460 1461
	}
}

1462 1463 1464 1465 1466 1467
#define CMP_AND_RETURN(n_a, n_b, name) \
	G_STMT_START { \
		gboolean _is = (strcmp (n_a, ""name) == 0); \
		\
		if (_is || (strcmp (n_b, ""name) == 0)) \
			return _is ? -1 : 1; \
1468
	} G_STMT_END
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488

static int
_enumerate_values_sort (GParamSpec **p_a, GParamSpec **p_b, GType *p_type)
{
	const char *n_a = (*p_a)->name;
	const char *n_b = (*p_b)->name;
	int c = strcmp (n_a, n_b);

	if (c) {
		if (*p_type == NM_TYPE_SETTING_CONNECTION) {
			/* for [connection], report first id, uuid, type in that order. */
			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_ID);
			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_UUID);
			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_TYPE);
		}
	}
	return c;
}
#undef CMP_AND_RETURN

1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
/**
 * nm_setting_enumerate_values:
 * @setting: the #NMSetting
 * @func: (scope call): user-supplied function called for each property of the setting
 * @user_data: user data passed to @func at each invocation
 *
 * Iterates over each property of the #NMSetting object, calling the supplied
 * user function for each property.
 **/
void
nm_setting_enumerate_values (NMSetting *setting,
                             NMSettingValueIterFn func,
                             gpointer user_data)
{
	GParamSpec **property_specs;
	guint n_property_specs;
	int i;
1506
	GType type;
1507 1508 1509 1510 1511

	g_return_if_fail (NM_IS_SETTING (setting));
	g_return_if_fail (func != NULL);

	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);