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

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

26 27
#include <string.h>

28
#include "nm-setting.h"
29
#include "nm-setting-private.h"
30
#include "nm-setting-connection.h"
31
#include "nm-utils.h"
32

Dan Williams's avatar
Dan Williams committed
33 34 35 36 37 38 39 40 41 42 43 44 45
/**
 * SECTION:nm-setting
 * @short_description: Describes related configuration information
 * @include: nm-setting.h
 *
 * Each #NMSetting contains properties that describe configuration that applies
 * to a specific network layer (like IPv4 or IPv6 configuration) or device type
 * (like Ethernet, or WiFi).  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.
 */

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/**
 * nm_setting_error_quark:
 *
 * Registers an error quark for #NMSetting if necessary.
 *
 * Returns: the error quark used for NMSetting errors.
 **/
GQuark
nm_setting_error_quark (void)
{
	static GQuark quark;

	if (G_UNLIKELY (!quark))
		quark = g_quark_from_static_string ("nm-setting-error-quark");
	return quark;
}

63
G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
64

65 66 67 68 69 70
#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))

typedef struct {
	char *name;
} NMSettingPrivate;

71 72 73
enum {
	PROP_0,
	PROP_NAME,
74

75 76
	PROP_LAST
};
77 78

static void
79
destroy_gvalue (gpointer data)
80
{
81
	GValue *value = (GValue *) data;
82

83 84
	g_value_unset (value);
	g_slice_free (GValue, value);
85 86
}

Dan Williams's avatar
Dan Williams committed
87 88 89
/**
 * nm_setting_to_hash:
 * @setting: the #NMSetting
90
 * @flags: hash flags, e.g. %NM_SETTING_HASH_FLAG_ALL
Dan Williams's avatar
Dan Williams committed
91 92 93
 *
 * Converts the #NMSetting into a #GHashTable mapping each setting property
 * name to a GValue describing that property, suitable for marshalling over
94
 * D-Bus or serializing.  The mapping is string to GValue.
Dan Williams's avatar
Dan Williams committed
95
 * 
96 97
 * Returns: (transfer full) (element-type utf8 GObject.Value): a new #GHashTable
 * describing the setting's properties
Dan Williams's avatar
Dan Williams committed
98
 **/
99
GHashTable *
100
nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
101
{
102 103 104 105
	GHashTable *hash;
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;
106

107
	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
108

109
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
Dan Williams's avatar
Dan Williams committed
110 111 112
	if (!property_specs) {
		g_warning ("%s: couldn't find property specs for object of type '%s'",
		           __func__, g_type_name (G_OBJECT_TYPE (setting)));
113
		return NULL;
Dan Williams's avatar
Dan Williams committed
114
	}
115

116
	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
117
	                              (GDestroyNotify) g_free, destroy_gvalue);
118

119 120
	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
121
		GValue *value;
122

123 124
		if (!(prop_spec->flags & NM_SETTING_PARAM_SERIALIZE))
			continue;
125

126 127 128
		if (   (flags & NM_SETTING_HASH_FLAG_NO_SECRETS)
		    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
			continue;
129

130 131 132 133 134 135 136 137 138 139 140 141 142
		if (   (flags & NM_SETTING_HASH_FLAG_ONLY_SECRETS)
		    && !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
			continue;

		value = g_slice_new0 (GValue);
		g_value_init (value, prop_spec->value_type);
		g_object_get_property (G_OBJECT (setting), prop_spec->name, value);

		/* Don't serialize values with default values */
		if (!g_param_value_defaults (prop_spec, value))
			g_hash_table_insert (hash, g_strdup (prop_spec->name), value);
		else
			destroy_gvalue (value);
143 144
	}
	g_free (property_specs);
145 146 147 148 149 150 151

	/* Don't return empty hashes */
	if (g_hash_table_size (hash) < 1) {
		g_hash_table_destroy (hash);
		hash = NULL;
	}

152
	return hash;
153 154
}

155 156 157 158 159
typedef struct {
	GObjectClass *class;
	guint n_params;
	GParameter *params;
} NMSettingFromHashInfo;
160 161

static void
162 163 164 165 166 167 168 169 170 171 172
one_property_cb (gpointer key, gpointer val, gpointer user_data)
{
	const char *prop_name = (char *) key;
	GValue *src_value = (GValue *) val;
	NMSettingFromHashInfo *info = (NMSettingFromHashInfo *) user_data;
	GValue *dst_value = &info->params[info->n_params].value;
	GParamSpec *param_spec;

	param_spec = g_object_class_find_property (info->class, prop_name);
	if (!param_spec || !(param_spec->flags & NM_SETTING_PARAM_SERIALIZE)) {
		/* Oh, we're so nice and only warn, maybe it should be a fatal error? */
173
		g_warning ("Ignoring invalid property '%s'", prop_name);
174 175
		return;
	}
176

177 178 179 180 181
	g_value_init (dst_value, G_VALUE_TYPE (src_value));
	if (g_value_transform (src_value, dst_value)) {
		info->params[info->n_params].name = prop_name;
		info->n_params++;
	} else {
182 183
		g_warning ("Ignoring property '%s' with invalid type (%s)",
		           prop_name, G_VALUE_TYPE_NAME (src_value));
184 185
		g_value_unset (dst_value);
	}
186 187
}

Dan Williams's avatar
Dan Williams committed
188 189 190
/**
 * nm_setting_new_from_hash:
 * @setting_type: the #NMSetting type which the hash contains properties for
191 192
 * @hash: (element-type utf8 GObject.Value): the #GHashTable containing a
 * string to GValue mapping of properties that apply to the setting
Dan Williams's avatar
Dan Williams committed
193 194 195 196 197 198 199 200 201 202 203
 *
 * Creates a new #NMSetting object and populates that object with the properties
 * contained in the hash table, using each hash key as the property to set,
 * and each hash value as the value to set that property to.  Setting properties
 * are strongly typed, thus the GValue type of the hash value must be correct.
 * See the documentation on each #NMSetting object subclass for the correct
 * property names and value types.
 * 
 * Returns: a new #NMSetting object populated with the properties from the
 * hash table, or NULL on failure
 **/
204
NMSetting *
205
nm_setting_new_from_hash (GType setting_type, GHashTable *hash)
206
{
207 208
	NMSetting *setting;
	NMSettingFromHashInfo info;
209
	int i;
210

211 212
	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
	g_return_val_if_fail (hash != NULL, NULL);
213

214 215 216
	info.class = g_type_class_ref (setting_type);
	info.n_params = 0;
	info.params = g_new0 (GParameter, g_hash_table_size (hash));
217

218
	g_hash_table_foreach (hash, one_property_cb, &info);
219

220
	setting = (NMSetting *) g_object_newv (setting_type, info.n_params, info.params);
221

222 223 224 225 226
	for (i = 0; i < info.n_params; i++) {
		GValue *v = &info.params[i].value;
		g_value_unset (v);
	}

227 228
	g_free (info.params);
	g_type_class_unref (info.class);
229

230
	return setting;
231 232
}

233 234
static void
duplicate_setting (NMSetting *setting,
235 236 237 238
                   const char *name,
                   const GValue *value,
                   GParamFlags flags,
                   gpointer user_data)
239
{
240 241
	if (flags & G_PARAM_WRITABLE)
		g_object_set_property (G_OBJECT (user_data), name, value);
242 243
}

Dan Williams's avatar
Dan Williams committed
244 245 246 247 248 249
/**
 * nm_setting_duplicate:
 * @setting: the #NMSetting to duplicate
 *
 * Duplicates a #NMSetting.
 *
250
 * Returns: (transfer full): a new #NMSetting containing the same properties and values as the
Dan Williams's avatar
Dan Williams committed
251 252
 * source #NMSetting
 **/
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
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);
}

Dan Williams's avatar
Dan Williams committed
269 270 271 272 273 274 275 276 277
/**
 * 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'.
 **/
278 279
const char *
nm_setting_get_name (NMSetting *setting)
280
{
281
	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
282

283
	return NM_SETTING_GET_PRIVATE (setting)->name;
284 285
}

Dan Williams's avatar
Dan Williams committed
286 287 288
/**
 * nm_setting_verify:
 * @setting: the #NMSetting to verify
289 290
 * @all_settings: (element-type NMSetting): a #GSList of all settings
 *     in the connection from which @setting came
Dan Williams's avatar
Dan Williams committed
291 292 293 294 295 296 297
 * @error: location to store error, or %NULL
 *
 * Validates the setting.  Each setting's properties have allowed values, and
 * some are dependent on other values (hence the need for @all_settings).  The
 * returned #GError contains information about which property of the setting
 * failed validation, and in what way that property failed validation.
 *
Dan Williams's avatar
Dan Williams committed
298
 * Returns: %TRUE if the setting is valid, %FALSE if it is not
Dan Williams's avatar
Dan Williams committed
299
 **/
300
gboolean
301
nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error)
302
{
303
	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
304 305
	if (error)
		g_return_val_if_fail (*error == NULL, FALSE);
306

307
	if (NM_SETTING_GET_CLASS (setting)->verify)
308
		return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error);
309

310
	return TRUE;
311 312
}

313 314 315 316 317
static gboolean
compare_property (NMSetting *setting,
	              NMSetting *other,
	              const GParamSpec *prop_spec,
	              NMSettingCompareFlags flags)
318
{
319 320
	GValue value1 = G_VALUE_INIT;
	GValue value2 = G_VALUE_INIT;
321
	gboolean different;
322

323 324 325 326
	/* 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;
327

328 329
		nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL);
		nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL);
330

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
		/* If the secret flags aren't the same the settings aren't the same */
		if (a_secret_flags != b_secret_flags)
			return FALSE;

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

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

	g_value_init (&value1, prop_spec->value_type);
	g_object_get_property (G_OBJECT (setting), prop_spec->name, &value1);

	g_value_init (&value2, prop_spec->value_type);
	g_object_get_property (G_OBJECT (other), prop_spec->name, &value2);

	different = g_param_values_cmp ((GParamSpec *) prop_spec, &value1, &value2);

	g_value_unset (&value1);
	g_value_unset (&value2);

	return different == 0 ? TRUE : FALSE;
359 360
}

Dan Williams's avatar
Dan Williams committed
361 362 363 364 365 366 367 368 369 370
/**
 * 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.
 *
Dan Williams's avatar
Dan Williams committed
371
 * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
Dan Williams's avatar
Dan Williams committed
372
 **/
373
gboolean
Dan Williams's avatar
Dan Williams committed
374 375
nm_setting_compare (NMSetting *a,
                    NMSetting *b,
376
                    NMSettingCompareFlags flags)
377
{
378 379
	GParamSpec **property_specs;
	guint n_property_specs;
380
	gint same = TRUE;
381
	guint i;
382

Dan Williams's avatar
Dan Williams committed
383 384
	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
	g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
385

386
	/* First check that both have the same type */
Dan Williams's avatar
Dan Williams committed
387
	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
388
		return FALSE;
389

390
	/* And now all properties */
Dan Williams's avatar
Dan Williams committed
391
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
392
	for (i = 0; i < n_property_specs && same; i++) {
393
		GParamSpec *prop_spec = property_specs[i];
394

395 396 397
		/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
		if (   (flags & NM_SETTING_COMPARE_FLAG_FUZZY)
			&& (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
398 399
			continue;

400 401 402 403 404 405 406
		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
		    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
			continue;

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

408 409
	return same;
}
410

411 412 413 414 415 416 417 418 419 420
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;
421

422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	if (prop_flags & NM_SETTING_PARAM_SECRET) {
		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;

		if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
			return FALSE;

		nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL);

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

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

439 440 441 442
	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
	    && NM_IS_SETTING_CONNECTION (setting)
	    && !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
		return FALSE;
443

444
	return TRUE;
445 446
}

447 448 449 450 451 452 453
/**
 * nm_setting_diff:
 * @a: a #NMSetting
 * @b: a second #NMSetting to compare with the first
 * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
 * @invert_results: this parameter is used internally by libnm-util and should
 * be set to %FALSE.  If %TRUE inverts the meaning of the #NMSettingDiffResult.
454 455 456 457 458
 * @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.
459 460 461 462 463 464 465 466 467 468 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 507 508 509 510 511 512
 *
 * Compares two #NMSetting objects for similarity, with comparison behavior
 * modified by a set of flags.  See the documentation for #NMSettingCompareFlags
 * for a description of each flag's behavior.  If the settings differ, the keys
 * of each setting that differ from the other are added to @results, mapped to
 * one or more #NMSettingDiffResult values.
 *
 * Returns: %TRUE if the settings contain the same values, %FALSE if they do not
 **/
gboolean
nm_setting_diff (NMSetting *a,
                 NMSetting *b,
                 NMSettingCompareFlags flags,
                 gboolean invert_results,
                 GHashTable **results)
{
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;
	NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
	NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
	gboolean results_created = FALSE;

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

	/* If the caller is calling this function in a pattern like this to get
	 * complete diffs:
	 *
	 * nm_setting_diff (A, B, FALSE, &results);
	 * nm_setting_diff (B, A, TRUE, &results);
	 *
	 * and wants us to invert the results so that the second invocation comes
	 * out correctly, do that here.
	 */
	if (invert_results) {
		a_result = NM_SETTING_DIFF_RESULT_IN_B;
		b_result = NM_SETTING_DIFF_RESULT_IN_A;
	}

	if (*results == NULL) {
		*results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
		results_created = TRUE;
	}

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

	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
513
		GValue a_value = G_VALUE_INIT, b_value = G_VALUE_INIT;
514 515 516 517 518 519 520 521 522 523 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 554 555 556 557 558
		NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN, tmp;
		gboolean different = TRUE;

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

		if (b) {
			g_value_init (&a_value, prop_spec->value_type);
			g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value);

			g_value_init (&b_value, prop_spec->value_type);
			g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value);

			different = !!g_param_values_cmp (prop_spec, &a_value, &b_value);
			if (different) {
				if (!g_param_value_defaults (prop_spec, &a_value))
					r |= a_result;
				if (!g_param_value_defaults (prop_spec, &b_value))
					r |= b_result;
			}

			g_value_unset (&a_value);
			g_value_unset (&b_value);
		} else
			r = a_result;  /* only in A */

		if (different) {
			tmp = GPOINTER_TO_UINT (g_hash_table_lookup (*results, prop_spec->name));
			g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (tmp | r));
		}
	}
	g_free (property_specs);

	/* Don't return an empty hash table */
	if (results_created && !g_hash_table_size (*results)) {
		g_hash_table_destroy (*results);
		*results = NULL;
	}

	return !(*results);
}

Dan Williams's avatar
Dan Williams committed
559 560 561
/**
 * nm_setting_enumerate_values:
 * @setting: the #NMSetting
562
 * @func: (scope call): user-supplied function called for each property of the setting
Dan Williams's avatar
Dan Williams committed
563 564 565 566 567
 * @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.
 **/
568 569 570 571
void
nm_setting_enumerate_values (NMSetting *setting,
					    NMSettingValueIterFn func,
					    gpointer user_data)
572
{
573 574
	GParamSpec **property_specs;
	guint n_property_specs;
575 576
	int i;

577 578
	g_return_if_fail (NM_IS_SETTING (setting));
	g_return_if_fail (func != NULL);
579

580 581 582
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
583
		GValue value = G_VALUE_INIT;
584

585 586
		g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
587
		func (setting, prop_spec->name, &value, prop_spec->flags, user_data);
588
		g_value_unset (&value);
589 590
	}

591
	g_free (property_specs);
592 593
}

Dan Williams's avatar
Dan Williams committed
594 595 596 597 598 599 600 601
/**
 * nm_setting_clear_secrets:
 * @setting: the #NMSetting
 *
 * Resets and clears any secrets in the setting.  Secrets should be added to the
 * setting only when needed, and cleared immediately after use to prevent
 * leakage of information.
 **/
602 603
void
nm_setting_clear_secrets (NMSetting *setting)
604
{
605 606 607
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;
608

609
	g_return_if_fail (NM_IS_SETTING (setting));
610

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

613 614
	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
615
		GValue value = G_VALUE_INIT;
616

617 618 619 620 621 622
		if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
			g_value_init (&value, prop_spec->value_type);
			g_param_value_set_default (prop_spec, &value);
			g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
			g_value_unset (&value);
		}
623 624
	}

625
	g_free (property_specs);
626 627
}

628 629 630 631 632 633
static void
clear_secrets_with_flags (NMSetting *setting,
	                      GParamSpec *pspec,
	                      NMSettingClearSecretsWithFlagsFn func,
	                      gpointer user_data)
{
634
	GValue value = G_VALUE_INIT;
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;

	/* Clear the secret if the user function says to do so */
	nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL);
	if (func (setting, pspec->name, flags, user_data) == TRUE) {
		g_value_init (&value, pspec->value_type);
		g_param_value_set_default (pspec, &value);
		g_object_set_property (G_OBJECT (setting), pspec->name, &value);
		g_value_unset (&value);
	}
}

/**
 * nm_setting_clear_secrets_with_flags:
 * @setting: the #NMSetting
650 651
 * @func: (scope call): function to be called to determine whether a
 *     specific secret should be cleared or not
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
 * @user_data: caller-supplied data passed to @func
 *
 * Clears and frees secrets determined by @func.
 **/
void
nm_setting_clear_secrets_with_flags (NMSetting *setting,
                                     NMSettingClearSecretsWithFlagsFn func,
                                     gpointer user_data)
{
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;

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

	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
	for (i = 0; i < n_property_specs; i++) {
		if (property_specs[i]->flags & NM_SETTING_PARAM_SECRET) {
			NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting,
			                                                          property_specs[i],
			                                                          func,
			                                                          user_data);
		}
	}

	g_free (property_specs);
}

Dan Williams's avatar
Dan Williams committed
682 683 684 685 686 687 688 689 690
/**
 * nm_setting_need_secrets:
 * @setting: the #NMSetting
 *
 * Returns an array of property names for each secret which may be required
 * to make a successful connection.  The returned hints are only intended as a
 * guide to what secrets may be required, because in some circumstances, there
 * is no way to conclusively determine exactly which secrets are needed.
 *
691
 * Returns: (transfer full) (element-type utf8): a #GPtrArray containing the property names of secrets of the
Dan Williams's avatar
Dan Williams committed
692 693 694 695
 * #NMSetting which may be required; the caller owns the array
 * and must free the each array element with g_free(), as well as the array
 * itself with g_ptr_array_free()
 **/
696 697
GPtrArray *
nm_setting_need_secrets (NMSetting *setting)
698
{
699
	GPtrArray *secrets = NULL;
700

701
	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
702

703 704
	if (NM_SETTING_GET_CLASS (setting)->need_secrets)
		secrets = NM_SETTING_GET_CLASS (setting)->need_secrets (setting);
705

706
	return secrets;
707 708
}

709 710
static gboolean
update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
711
{
712
	GParamSpec *prop_spec;
713
	GValue transformed_value = G_VALUE_INIT;
714
	gboolean success = FALSE;
715

716
	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
717
	if (!prop_spec) {
718 719 720 721 722
		g_set_error (error,
		             NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
		             "%s", key);
		return FALSE;
723 724
	}

725 726 727
	/* Silently ignore non-secrets */
	if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET))
		return TRUE;
Dan Williams's avatar
Dan Williams committed
728

729
	if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) {
730
		g_object_set_property (G_OBJECT (setting), prop_spec->name, value);
731 732
		success = TRUE;
	} else if (g_value_transform (value, &transformed_value)) {
733 734
		g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value);
		g_value_unset (&transformed_value);
735
		success = TRUE;
736
	} else {
737 738 739 740
		g_set_error (error,
		             NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
		             "%s", key);
741
	}
742
	return success;
743 744
}

745 746 747
/**
 * nm_setting_update_secrets:
 * @setting: the #NMSetting
748 749
 * @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping
 * string to #GValue of setting property names and secrets
750 751 752 753 754
 * @error: location to store error, or %NULL
 *
 * Update the setting's secrets, given a hash table of secrets intended for that
 * setting (deserialized from D-Bus for example).
 * 
Dan Williams's avatar
Dan Williams committed
755 756
 * Returns: %TRUE if the secrets were successfully updated and the connection
 * is valid, %FALSE on failure or if the setting was never added to the connection
757 758 759
 **/
gboolean
nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
760
{
761 762 763
	GHashTableIter iter;
	gpointer key, data;
	GError *tmp_error = NULL;
764 765 766 767 768 769

	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
	g_return_val_if_fail (secrets != NULL, FALSE);
	if (error)
		g_return_val_if_fail (*error == NULL, FALSE);

770
	g_hash_table_iter_init (&iter, secrets);
771
	while (g_hash_table_iter_next (&iter, &key, &data)) {
772 773
		const char *secret_key = (const char *) key;
		GValue *secret_value = (GValue *) data;
774

775
		NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
776 777
		if (tmp_error) {
			g_propagate_error (error, tmp_error);
778
			return FALSE;
779
		}
780 781
	}

782
	return TRUE;
783 784
}

785 786 787 788 789
static gboolean
is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
{
	GParamSpec *pspec;

790
	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), secret_name);
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
	if (!pspec) {
		g_set_error (error,
		             NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
		             "Secret %s not provided by this setting", secret_name);
		return FALSE;
	}

	if (!(pspec->flags & NM_SETTING_PARAM_SECRET)) {
		g_set_error (error,
		             NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
		             "Property %s is not a secret", secret_name);
		return FALSE;
	}

	return TRUE;
}

static gboolean
get_secret_flags (NMSetting *setting,
                  const char *secret_name,
813
                  gboolean verify_secret,
814 815 816 817
                  NMSettingSecretFlags *out_flags,
                  GError **error)
{
	char *flags_prop;
818
	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
819

820 821
	if (verify_secret)
		g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853

	flags_prop = g_strdup_printf ("%s-flags", secret_name);
	g_object_get (G_OBJECT (setting), flags_prop, &flags, NULL);
	g_free (flags_prop);

	if (out_flags)
		*out_flags = flags;
	return TRUE;
}

/**
 * nm_setting_get_secret_flags:
 * @setting: the #NMSetting
 * @secret_name: the secret key name to get flags for
 * @out_flags: on success, the #NMSettingSecretFlags for the secret
 * @error: location to store error, or %NULL
 *
 * For a given secret, retrieves the #NMSettingSecretFlags describing how to
 * handle that secret.
 *
 * Returns: TRUE on success (if the given secret name was a valid property of
 * this setting, and if that property is secret), FALSE if not
 **/
gboolean
nm_setting_get_secret_flags (NMSetting *setting,
                             const char *secret_name,
                             NMSettingSecretFlags *out_flags,
                             GError **error)
{
	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
	g_return_val_if_fail (secret_name != NULL, FALSE);

854
	return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, TRUE, out_flags, error);
855 856 857 858 859
}

static gboolean
set_secret_flags (NMSetting *setting,
                  const char *secret_name,
860
                  gboolean verify_secret,
861 862 863 864 865
                  NMSettingSecretFlags flags,
                  GError **error)
{
	char *flags_prop;

866 867
	if (verify_secret)
		g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895

	flags_prop = g_strdup_printf ("%s-flags", secret_name);
	g_object_set (G_OBJECT (setting), flags_prop, flags, NULL);
	g_free (flags_prop);
	return TRUE;
}

/**
 * nm_setting_set_secret_flags:
 * @setting: the #NMSetting
 * @secret_name: the secret key name to set flags for
 * @flags: the #NMSettingSecretFlags for the secret
 * @error: location to store error, or %NULL
 *
 * For a given secret, retrieves the #NMSettingSecretFlags describing how to
 * handle that secret.
 *
 * Returns: TRUE on success (if the given secret name was a valid property of
 * this setting, and if that property is secret), FALSE if not
 **/
gboolean
nm_setting_set_secret_flags (NMSetting *setting,
                             const char *secret_name,
                             NMSettingSecretFlags flags,
                             GError **error)
{
	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
	g_return_val_if_fail (secret_name != NULL, FALSE);
896
	g_return_val_if_fail (flags <= NM_SETTING_SECRET_FLAGS_ALL, FALSE);
897

898
	return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, TRUE, flags, error);
899 900
}

Dan Williams's avatar
Dan Williams committed
901 902 903 904 905 906 907 908 909 910 911 912
/**
 * nm_setting_to_string:
 * @setting: the #NMSetting
 *
 * Convert the setting into a string.  For debugging purposes ONLY, should NOT
 * be used for serialization of the setting, or machine-parsed in any way. The
 * output format is not guaranteed to be stable and may change at any time.
 *
 * Returns: an allocated string containing a textual representation of the
 * setting's properties and values (including secrets!), which the caller should
 * free with g_free()
 **/
913 914
char *
nm_setting_to_string (NMSetting *setting)
915
{
916 917 918 919
	GString *string;
	GParamSpec **property_specs;
	guint n_property_specs;
	guint i;
920

921
	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
922

923 924 925
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
	if (!property_specs)
		return NULL;
926

927
	string = g_string_new (nm_setting_get_name (setting));
928
	g_string_append_c (string, '\n');
929

930 931
	for (i = 0; i < n_property_specs; i++) {
		GParamSpec *prop_spec = property_specs[i];
932
		GValue value = G_VALUE_INIT;
933 934 935
		char *value_str;
		gboolean is_serializable;
		gboolean is_default;
936

937 938
		g_value_init (&value, prop_spec->value_type);
		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
939

940 941 942
		value_str = g_strdup_value_contents (&value);
		g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str);
		g_free (value_str);
943

944 945
		is_serializable = prop_spec->flags & NM_SETTING_PARAM_SERIALIZE;
		is_default = g_param_value_defaults (prop_spec, &value);
946

947 948
		g_value_unset (&value);

949 950
		if (is_serializable || is_default) {
			g_string_append (string, " (");
951

952 953 954 955
			if (is_serializable)
				g_string_append_c (string, 's');
			if (is_default)
				g_string_append_c (string, 'd');
956

957
			g_string_append_c (string, ')');
958
		}
959

960
		g_string_append_c (string, '\n');
961 962
	}

963 964
	g_free (property_specs);
	g_string_append_c (string, '\n');
965

966
	return g_string_free (string, FALSE);
967 968
}

969 970 971 972 973 974 975
/**
 * nm_setting_get_virtual_iface_name:
 * @setting: the #NMSetting
 *
 * Returns the name of the virtual kernel interface which the connection
 * needs to use if specified in the settings.
 *
976 977
 * Returns: Name of the virtual interface or %NULL if the setting does not
 * support this feature
978 979 980 981 982 983 984 985 986 987 988 989
 **/
const char *
nm_setting_get_virtual_iface_name (NMSetting *setting)
{
	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);

	if (NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name)
		return NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name (setting);

	return NULL;
}

990
/*****************************************************************************/
991

992 993
static void
nm_setting_init (NMSetting *setting)
994
{
995 996
}

997 998 999 1000
static GObject*
constructor (GType type,
		   guint n_construct_params,
		   GObjectConstructParam *construct_params)
1001
{
1002
	GObject *object;
1003
	NMSettingPrivate *priv;
1004

1005 1006 1007 1008 1009
	object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type,
													    n_construct_params,
													    construct_params);
	if (!object)
		return NULL;
1010

1011 1012
	priv = NM_SETTING_GET_PRIVATE (object);
	if (!priv->name) {
1013
		g_warning ("Setting name is not set.");
1014 1015
		g_object_unref (object);
		object = NULL;
1016 1017
	}

1018
	return object;
1019 1020 1021
}

static void
1022
finalize (GObject *object)
1023
{
1024
	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
1025

1026
	g_free (priv->name);
1027