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

#include "nm-default.h"

#include "nm-meta-setting-desc.h"

#include <stdlib.h>
#include <arpa/inet.h>

#include "nm-common-macros.h"
#include "nm-utils/nm-hash-utils.h"
#include "nm-utils/nm-enum-utils.h"

#include "NetworkManager.h"
#include "nm-vpn-helpers.h"
#include "nm-client-utils.h"
#include "nm-meta-setting-access.h"

/*****************************************************************************/

static char *secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type);

#define ALL_SECRET_FLAGS \
	(NM_SETTING_SECRET_FLAG_NONE | \
	 NM_SETTING_SECRET_FLAG_AGENT_OWNED | \
	 NM_SETTING_SECRET_FLAG_NOT_SAVED | \
	 NM_SETTING_SECRET_FLAG_NOT_REQUIRED)

/*****************************************************************************/

static GType
_gobject_property_get_gtype (GObject *gobject, const char *property_name)
{
	GParamSpec *param_spec;

	param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (gobject), property_name);
	if (param_spec)
		return param_spec->value_type;
	g_return_val_if_reached (G_TYPE_INVALID);
}

static GType
_gtype_property_get_gtype (GType gtype, const char *property_name)
{
	/* given @gtype, a type for a GObject, lookup the property @property_name
	 * and return its value_type. */
	if (G_TYPE_IS_CLASSED (gtype)) {
		GParamSpec *param_spec;
		nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype);

		if (G_IS_OBJECT_CLASS (gtypeclass)) {
			param_spec = g_object_class_find_property (G_OBJECT_CLASS (gtypeclass), property_name);
			if (param_spec)
				return param_spec->value_type;
		}
	}
	g_return_val_if_reached (G_TYPE_INVALID);
}

/*****************************************************************************/

static NMIPAddress *
_parse_ip_address (int family, const char *address, GError **error)
{
	gs_free char *ip_str = NULL;
	const int MAX_PREFIX = (family == AF_INET) ? 32 : 128;
	NMIPAddress *addr;
	char *plen;
	int prefix;
	GError *local = NULL;

	g_return_val_if_fail (address, NULL);
	g_return_val_if_fail (!error || !*error, NULL);

	ip_str = g_strstrip (g_strdup (address));

	prefix = MAX_PREFIX;

	plen = strchr (ip_str, '/');
	if (plen) {
		*plen++ = '\0';
		if ((prefix = _nm_utils_ascii_str_to_int64 (plen, 10, 1, MAX_PREFIX, -1)) == -1) {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("invalid prefix '%s'; <1-%d> allowed"), plen, MAX_PREFIX);
			return NULL;
		}
	}

	addr = nm_ip_address_new (family, ip_str, prefix, &local);
	if (!addr) {
		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
		             _("invalid IP address: %s"), local->message);
		g_clear_error (&local);
	}
	return addr;
}

static NMIPRoute *
_parse_ip_route (int family,
                 const char *str,
                 GError **error)
{
	const int MAX_PREFIX = (family == AF_INET) ? 32 : 128;
	char *plen = NULL;
	const char *next_hop = NULL;
	const char *canon_dest;
	int prefix;
	NMIPRoute *route = NULL;
	GError *local = NULL;
	gint64 metric = -1;
	guint i;
	gs_strfreev char **routev = NULL;
	gs_free char *str_clean = NULL;
	char *dest;
	gs_unref_hashtable GHashTable *attrs = NULL;
	GHashTable *tmp_attrs;
#define ROUTE_SYNTAX _("The valid syntax is: 'ip[/prefix] [next-hop] [metric] [attribute=val]... [,ip[/prefix] ...]'")

	nm_assert (NM_IN_SET (family, AF_INET, AF_INET6));
	nm_assert (str);
	nm_assert (!error || !*error);

	str_clean = g_strstrip (g_strdup (str));
	routev = nmc_strsplit_set (str_clean, " \t", 0);
	if (!routev || !routev[0]) {
		g_set_error (error, 1, 0,
		             "'%s' is not valid. %s",
		             str, ROUTE_SYNTAX);
		return NULL;
	}

	dest = routev[0];
	plen = strchr (dest, '/');  /* prefix delimiter */
	if (plen)
		*plen++ = '\0';
	prefix = MAX_PREFIX;
	if (plen) {
		if ((prefix = _nm_utils_ascii_str_to_int64 (plen, 10, 1, MAX_PREFIX, -1)) == -1) {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("invalid prefix '%s'; <1-%d> allowed"),
			             plen, MAX_PREFIX);
			return NULL;
		}
	}

	for (i = 1; routev[i]; i++) {
		gint64 tmp64;

		if (nm_utils_ipaddr_valid (family, routev[i])) {
			if (metric != -1 || attrs) {
				g_set_error (error, 1, 0, _("the next hop ('%s') must be first"), routev[i]);
				return NULL;
			}
			next_hop = routev[i];
		} else if ((tmp64 = _nm_utils_ascii_str_to_int64 (routev[i], 10, 0, G_MAXUINT32, -1)) != -1) {
			if (attrs) {
				g_set_error (error, 1, 0, _("the metric ('%s') must be before attributes"), routev[i]);
				return NULL;
			}
			metric = tmp64;
		} else if (strchr (routev[i], '=')) {
			GHashTableIter iter;
			char *iter_key;
			GVariant *iter_value;

			tmp_attrs = nm_utils_parse_variant_attributes (routev[i], ' ', '=', FALSE,
			                                               nm_ip_route_get_variant_attribute_spec(),
			                                               error);
			if (!tmp_attrs) {
				g_prefix_error (error, "invalid option '%s': ", routev[i]);
				return NULL;
			}

			if (!attrs)
				attrs = g_hash_table_new (nm_str_hash, g_str_equal);

			g_hash_table_iter_init (&iter, tmp_attrs);
			while (g_hash_table_iter_next (&iter, (gpointer *) &iter_key, (gpointer *) &iter_value)) {
				if (!nm_ip_route_attribute_validate (iter_key, iter_value, family, NULL, error)) {
					g_prefix_error (error, "%s: ", iter_key);
					g_hash_table_unref (tmp_attrs);
					return NULL;
				}
				g_hash_table_insert (attrs, iter_key, iter_value);
				g_hash_table_iter_steal (&iter);
			}
			g_hash_table_unref (tmp_attrs);
		} else {
			g_set_error (error, 1, 0, "%s", ROUTE_SYNTAX);
			return NULL;
		}
	}

	route = nm_ip_route_new (family, dest, prefix, next_hop, metric, &local);
	if (!route) {
		g_set_error (error, 1, 0,
		             _("invalid route: %s. %s"), local->message, ROUTE_SYNTAX);
		g_clear_error (&local);
		return NULL;
	}

	/* We don't accept default routes as NetworkManager handles it
	 * itself. But we have to check this after @route has normalized the
	 * dest string.
	 */
	canon_dest = nm_ip_route_get_dest (route);
	if (!strcmp (canon_dest, "0.0.0.0") || !strcmp (canon_dest, "::")) {
		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
		                     _("default route cannot be added (NetworkManager handles it by itself)"));
		g_clear_pointer (&route, nm_ip_route_unref);
		return NULL;
	}

	if (attrs) {
		GHashTableIter iter;
		char *name;
		GVariant *variant;

		g_hash_table_iter_init (&iter, attrs);
		while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant))
			nm_ip_route_set_attribute (route, name, variant);
	}

	return route;
}

static char *
_dump_team_link_watcher (NMTeamLinkWatcher *watcher)
{
	const char *name;
	NMTeamLinkWatcherArpPingFlags flags;
	GString *w_dump;

	if (!watcher)
		return NULL;

	w_dump = g_string_new (NULL);
	name = nm_team_link_watcher_get_name (watcher);
	g_string_append_printf (w_dump, "name=%s", name);

#define DUMP_WATCHER_INT(str, watcher, name, key) \
	G_STMT_START { \
		int _val = nm_team_link_watcher_get_##key (watcher); \
		\
		if (_val) \
			g_string_append_printf (str, " %s=%d", name, _val); \
	} G_STMT_END;

	if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
		DUMP_WATCHER_INT (w_dump, watcher, "delay-up", delay_up);
		DUMP_WATCHER_INT (w_dump, watcher, "delay-down", delay_down);
		return g_string_free (w_dump, FALSE);
	}
	/* NM_TEAM_LINK_WATCHER_NSNA_PING and NM_TEAM_LINK_WATCHER_ARP_PING */
	DUMP_WATCHER_INT (w_dump, watcher, "init-wait", init_wait);
	DUMP_WATCHER_INT (w_dump, watcher, "initerval", interval);
	DUMP_WATCHER_INT (w_dump, watcher, "missed-max", missed_max);
#undef DUMP_WATCHER_INT
	g_string_append_printf (w_dump, " target-host=%s",
	                        nm_team_link_watcher_get_target_host (watcher));

	if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
		return g_string_free (w_dump, FALSE);

	g_string_append_printf (w_dump, " source-host=%s",
	                        nm_team_link_watcher_get_source_host (watcher));
	flags = nm_team_link_watcher_get_flags (watcher);
	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE)
		g_string_append_printf (w_dump, " validate-active=true");
	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE)
		g_string_append_printf (w_dump, " validate-inactive=true");
	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS)
		g_string_append_printf (w_dump, "send-always=true");

	return g_string_free (w_dump, FALSE);
}

static NMTeamLinkWatcher *
_parse_team_link_watcher (const char *str,
                          GError **error)
{
	gs_strfreev char **watcherv = NULL;
	gs_free char *str_clean = NULL;
	guint i;
	gs_free const char *name = NULL;
	int val1 = 0, val2 = 0, val3 = 3;
	gs_free const char *target_host = NULL;
	gs_free const char *source_host = NULL;
	NMTeamLinkWatcherArpPingFlags flags = 0;

	nm_assert (str);
	nm_assert (!error || !*error);

	str_clean = g_strstrip (g_strdup (str));
	watcherv = nmc_strsplit_set (str_clean, " \t", 0);
	if (!watcherv || !watcherv[0]) {
		g_set_error (error, 1, 0, "'%s' is not valid", str);
		return NULL;
	}

	for (i = 0; watcherv[i]; i++) {
		gs_strfreev char **pair = NULL;

		pair = nmc_strsplit_set (watcherv[i], "=", 0);
		if (!pair[0]) {
			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
			             "properties should be specified as 'key=value'");
			return NULL;
		}
		if (!pair[1]) {
			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
			             "missing key value");
			return NULL;
		}
		if (pair[2]) {
			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
			             "properties should be specified as 'key=value'");
			return NULL;
		}

		if (nm_streq (pair[0], "name"))
			name = g_strdup (pair[1]);
		else if (   nm_streq (pair[0], "delay-up")
		         || nm_streq (pair[0], "init-wait"))
			val1 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
		else if (   nm_streq (pair[0], "delay-down")
		         || nm_streq (pair[0], "interval"))
			val2 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
		else if (nm_streq (pair[0], "missed-max"))
			val3 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
		else if (nm_streq (pair[0], "target-host"))
			target_host = g_strdup (pair[1]);
		else if (nm_streq (pair[0], "source-host"))
			source_host = g_strdup (pair[1]);
		else if (nm_streq (pair[0], "validate-active")) {
			if (nm_streq (pair[1], "true"))
				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
		} else if (nm_streq (pair[0], "validate-inactive")) {
			if (nm_streq (pair[1], "true"))
				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
		} else if (nm_streq (pair[0], "send-always")) {
			if (nm_streq (pair[1], "true"))
				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
		} else {
			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
			             "unknown key");
			return NULL;
		}

		if ((val1 < 0) || (val2 < 0) || (val3 < 0)) {
			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
			             "value is not a valid number [0, MAXINT]");
			return NULL;
		}
	}

	if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL))
		return nm_team_link_watcher_new_ethtool (val1, val2, error);
	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
		return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, error);
	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING))
		return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, source_host, flags, error);

	if (!name)
		g_set_error (error, 1, 0, "link watcher name missing");
	else
		g_set_error (error, 1, 0, "unknown link watcher name: '%s'", name);
	return NULL;
}

/* Max priority values from libnm-core/nm-setting-vlan.c */
#define MAX_SKB_PRIO   G_MAXUINT32
#define MAX_8021P_PRIO 7  /* Max 802.1p priority */

/*
 * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3
 * and verify if the priority numbers are valid
 *
 * Return: string array with split maps, or NULL on error
 * Caller is responsible for freeing the array.
 */
static char **
_parse_vlan_priority_maps (const char *priority_map,
                           NMVlanPriorityMap map_type,
                           GError **error)
{
	char **mapping = NULL, **iter;
	unsigned long from, to, from_max, to_max;

	g_return_val_if_fail (priority_map != NULL, NULL);
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);

	if (map_type == NM_VLAN_INGRESS_MAP) {
		from_max = MAX_8021P_PRIO;
		to_max = MAX_SKB_PRIO;
	} else {
		from_max = MAX_SKB_PRIO;
		to_max = MAX_8021P_PRIO;
	}

	mapping = g_strsplit (priority_map, ",", 0);
	for (iter = mapping; iter && *iter; iter++) {
		char *left, *right;

		left = g_strstrip (*iter);
		right = strchr (left, ':');
		if (!right) {
			g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter);
			g_strfreev (mapping);
			return NULL;
		}
		*right++ = '\0';

		if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) {
			g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"),
			             left, from_max);
			g_strfreev (mapping);
			return NULL;
		}
		if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) {
			g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"),
			             right, to_max);
			g_strfreev (mapping);
			return NULL;
		}
		*(right-1) = ':'; /* Put back ':' */
	}
	return mapping;
}

/*
 * nmc_proxy_check_script:
 * @script: file name with PAC script, or raw PAC Script data
 * @out_script: raw PAC Script (with removed new-line characters)
 * @error: location to store error, or %NULL
 *
 * Check PAC Script from @script parameter and return the checked/sanitized
 * config in @out_script.
 *
 * Returns: %TRUE if the script is valid, %FALSE if it is invalid
 */
static gboolean
nmc_proxy_check_script (const char *script, char **out_script, GError **error)
{
	enum {
		_PAC_SCRIPT_TYPE_GUESS,
		_PAC_SCRIPT_TYPE_FILE,
		_PAC_SCRIPT_TYPE_JSON,
	} desired_type = _PAC_SCRIPT_TYPE_GUESS;
	const char *filename = NULL;
	size_t c_len = 0;
	gs_free char *script_clone = NULL;

	*out_script = NULL;

	if (!script || !script[0])
		return TRUE;

	if (g_str_has_prefix (script, "file://")) {
		script += NM_STRLEN ("file://");
		desired_type = _PAC_SCRIPT_TYPE_FILE;
	} else if (g_str_has_prefix (script, "js://")) {
		script += NM_STRLEN ("js://");
		desired_type = _PAC_SCRIPT_TYPE_JSON;
	}

	if (NM_IN_SET (desired_type, _PAC_SCRIPT_TYPE_FILE, _PAC_SCRIPT_TYPE_GUESS)) {
		gs_free char *contents = NULL;

		if (!g_file_get_contents (script, &contents, &c_len, NULL)) {
			if (desired_type == _PAC_SCRIPT_TYPE_FILE) {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("cannot read pac-script from file '%s'"),
				             script);
				return FALSE;
			}
		} else {
			if (c_len != strlen (contents)) {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("file '%s' contains non-valid utf-8"),
				             script);
				return FALSE;
			}
			filename = script;
			script = script_clone = g_steal_pointer (&contents);
		}
	}

	if (   !strstr (script, "FindProxyForURL")
	    || !g_utf8_validate (script, -1, NULL)) {
		if (filename) {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("'%s' does not contain a valid PAC Script"), filename);
		} else {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("Not a valid PAC Script"));
		}
		return FALSE;
	}

	*out_script = (script == script_clone)
	              ? g_steal_pointer (&script_clone)
	              : g_strdup (script);
	return TRUE;
}

/*
 * nmc_team_check_config:
 * @config: file name with team config, or raw team JSON config data
 * @out_config: raw team JSON config data
 *   The value must be freed with g_free().
 * @error: location to store error, or %NUL
 *
 * Check team config from @config parameter and return the checked
 * config in @out_config.
 *
 * Returns: %TRUE if the config is valid, %FALSE if it is invalid
 */
static gboolean
nmc_team_check_config (const char *config, char **out_config, GError **error)
{
	enum {
		_TEAM_CONFIG_TYPE_GUESS,
		_TEAM_CONFIG_TYPE_FILE,
		_TEAM_CONFIG_TYPE_JSON,
	} desired_type = _TEAM_CONFIG_TYPE_GUESS;
	const char *filename = NULL;
	size_t c_len = 0;
	gs_free char *config_clone = NULL;

	*out_config = NULL;

	if (!config || !config[0])
		return TRUE;

	if (g_str_has_prefix (config, "file://")) {
		config += NM_STRLEN ("file://");
		desired_type = _TEAM_CONFIG_TYPE_FILE;
	} else if (g_str_has_prefix (config, "json://")) {
		config += NM_STRLEN ("json://");
		desired_type = _TEAM_CONFIG_TYPE_JSON;
	}

	if (NM_IN_SET (desired_type, _TEAM_CONFIG_TYPE_FILE, _TEAM_CONFIG_TYPE_GUESS)) {
		gs_free char *contents = NULL;

		if (!g_file_get_contents (config, &contents, &c_len, NULL)) {
			if (desired_type == _TEAM_CONFIG_TYPE_FILE) {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("cannot read team config from file '%s'"),
				             config);
				return FALSE;
			}
		} else {
			if (c_len != strlen (contents)) {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("team config file '%s' contains non-valid utf-8"),
				             config);
				return FALSE;
			}
			filename = config;
			config = config_clone = g_steal_pointer (&contents);
		}
	}

	if (!nm_utils_is_json_object (config, NULL)) {
		if (filename) {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("'%s' does not contain a valid team configuration"), filename);
		} else {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("team configuration must be a JSON object"));
		}
		return FALSE;
	}

	*out_config = (config == config_clone)
	              ? g_steal_pointer (&config_clone)
	              : g_strdup (config);
	return TRUE;
}

static const char *
_get_text_hidden (NMMetaAccessorGetType get_type)
{
	if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
		return _(NM_META_TEXT_HIDDEN);
	return NM_META_TEXT_HIDDEN;
}


/*****************************************************************************/

G_GNUC_PRINTF (4, 5)
static void
_env_warn_fcn (const NMMetaEnvironment *environment,
               gpointer environment_user_data,
               NMMetaEnvWarnLevel warn_level,
               const char *fmt_l10n,
               ...)
{
	va_list ap;

	if (!environment || !environment->warn_fcn)
		return;

	va_start (ap, fmt_l10n);
	environment->warn_fcn (environment,
	                       environment_user_data,
	                       warn_level,
	                       fmt_l10n,
	                       ap);
	va_end (ap);
}

/*****************************************************************************/

#define ARGS_DESCRIBE_FCN \
	const NMMetaPropertyInfo *property_info, char **out_to_free

#define ARGS_GET_FCN \
	const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, NMMetaAccessorGetType get_type, NMMetaAccessorGetFlags get_flags, NMMetaAccessorGetOutFlags *out_flags, gpointer *out_to_free

#define ARGS_SET_FCN \
	const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, GError **error

#define ARGS_REMOVE_FCN \
	const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, guint32 idx, GError **error

#define ARGS_COMPLETE_FCN \
	const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, const NMMetaOperationContext *operation_context, const char *text, char ***out_to_free

#define ARGS_VALUES_FCN \
	const NMMetaPropertyInfo *property_info, char ***out_to_free

#define ARGS_SETTING_INIT_FCN \
	const NMMetaSettingInfoEditor *setting_info, NMSetting *setting, NMMetaAccessorSettingInitType init_type

#define RETURN_UNSUPPORTED_GET_TYPE() \
	G_STMT_START { \
		if (!NM_IN_SET (get_type, \
		                NM_META_ACCESSOR_GET_TYPE_PARSABLE, \
		                NM_META_ACCESSOR_GET_TYPE_PRETTY)) { \
			nm_assert_not_reached (); \
			return NULL; \
		} \
	} G_STMT_END;

#define RETURN_STR_TO_FREE(val) \
	G_STMT_START { \
		char *_val = (val); \
		return ((*(out_to_free)) = _val); \
	} G_STMT_END

static gconstpointer
_get_fcn_nmc_with_default (ARGS_GET_FCN)
{
	const char *s;
	char *s_full;
	GValue val = G_VALUE_INIT;

	RETURN_UNSUPPORTED_GET_TYPE ();

	if (property_info->property_typ_data->subtype.get_with_default.fcn (setting)) {
		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
			return _("(default)");
		return "";
	}

	g_value_init (&val, G_TYPE_STRING);
	g_object_get_property (G_OBJECT (setting), property_info->property_name, &val);
	s = g_value_get_string (&val);
	if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
		s_full = s ? g_strdup_printf ("\"%s\"", s) : g_strdup ("");
	else
		s_full = g_strdup (s && *s ? s : " ");
	g_value_unset (&val);
	RETURN_STR_TO_FREE (s_full);
}

static gconstpointer
_get_fcn_gobject_impl (const NMMetaPropertyInfo *property_info,
                       NMSetting *setting,
                       NMMetaAccessorGetType get_type,
                       gpointer *out_to_free)
{
	char *s;
	const char *s_c;
	GType gtype_prop;
	nm_auto_unset_gvalue GValue val = G_VALUE_INIT;

	RETURN_UNSUPPORTED_GET_TYPE ();

	gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name);

	if (gtype_prop == G_TYPE_BOOLEAN) {
		gboolean b;

		g_value_init (&val, gtype_prop);
		g_object_get_property (G_OBJECT (setting), property_info->property_name, &val);
		b = g_value_get_boolean (&val);
		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
			s_c = b ? _("yes") : _("no");
		else
			s_c = b ? "yes" : "no";
		return s_c;
	} else {
		g_value_init (&val, G_TYPE_STRING);
		g_object_get_property (G_OBJECT (setting), property_info->property_name, &val);
		s = g_value_dup_string (&val);
		RETURN_STR_TO_FREE (s);
	}
}

static gconstpointer
_get_fcn_gobject (ARGS_GET_FCN)
{
	return _get_fcn_gobject_impl (property_info, setting, get_type, out_to_free);
}

static gconstpointer
_get_fcn_gobject_int (ARGS_GET_FCN)
{
	const GParamSpec *pspec;
	nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
	gint64 v;
	const NMMetaUtilsIntValueInfo *value_infos;

	RETURN_UNSUPPORTED_GET_TYPE ();

	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), property_info->property_name);
	if (!G_IS_PARAM_SPEC (pspec))
		g_return_val_if_reached (FALSE);

	g_value_init (&gval, pspec->value_type);
	g_object_get_property (G_OBJECT (setting), property_info->property_name, &gval);
	switch (pspec->value_type) {
	case G_TYPE_INT:
		v = g_value_get_int (&gval);
		break;
	case G_TYPE_UINT:
		v = g_value_get_uint (&gval);
		break;
	case G_TYPE_INT64:
		v = g_value_get_int64 (&gval);
		break;
	default:
		g_return_val_if_reached (NULL);
		break;
	}

	if (   get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY
	    && property_info->property_typ_data
	    && (value_infos = property_info->property_typ_data->subtype.gobject_int.value_infos)) {
		for (; value_infos->nick; value_infos++) {
			if (value_infos->value == v) {
				RETURN_STR_TO_FREE (g_strdup_printf ("%lli (%s)",
				                                     (long long) v,
				                                     value_infos->nick));
			}
		}
	}

	RETURN_STR_TO_FREE (g_strdup_printf ("%"G_GINT64_FORMAT, v));
}

static gconstpointer
_get_fcn_gobject_mtu (ARGS_GET_FCN)
{
	guint32 mtu;

	RETURN_UNSUPPORTED_GET_TYPE ();

	if (   !property_info->property_typ_data
	    || !property_info->property_typ_data->subtype.mtu.get_fcn)
		return _get_fcn_gobject_impl (property_info, setting, get_type, out_to_free);

	mtu = property_info->property_typ_data->subtype.mtu.get_fcn (setting);
	if (mtu == 0) {
		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
			return _("auto");
		return "auto";
	}
	RETURN_STR_TO_FREE (g_strdup_printf ("%u", (unsigned) mtu));
}

static gconstpointer
_get_fcn_gobject_secret_flags (ARGS_GET_FCN)
{
	guint v;
	GValue val = G_VALUE_INIT;

	RETURN_UNSUPPORTED_GET_TYPE ();

	g_value_init (&val, G_TYPE_UINT);
	g_object_get_property (G_OBJECT (setting), property_info->property_name, &val);
	v = g_value_get_uint (&val);
	g_value_unset (&val);
	RETURN_STR_TO_FREE (secret_flags_to_string (v, get_type));
}

static gconstpointer
_get_fcn_gobject_enum (ARGS_GET_FCN)
{
	GType gtype = 0;
	GType gtype_prop;
	nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL;
	nm_auto_unref_gtypeclass GTypeClass *gtype_prop_class = NULL;
	gboolean has_gtype = FALSE;
	nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
	gint64 v;
	gboolean format_numeric = FALSE;
	gboolean format_numeric_hex = FALSE;
	gboolean format_numeric_hex_unknown = FALSE;
	gboolean format_text = FALSE;
	gboolean format_text_l10n = FALSE;
	gs_free char *s = NULL;
	char s_numeric[64];

	RETURN_UNSUPPORTED_GET_TYPE ();

	if (property_info->property_typ_data) {
		if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) {
			gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype ();
			has_gtype = TRUE;
		}
	}

	if (   property_info->property_typ_data
	    && get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY
	    && NM_FLAGS_ANY (property_info->property_typ_data->typ_flags,   NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC
	                                                                  | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX
	                                                                  | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT
	                                                                  | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N)) {
		format_numeric_hex = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX);
		format_numeric = format_numeric_hex || NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC);
		format_text_l10n = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N);
		format_text = format_text_l10n || NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT);
	} else if (   property_info->property_typ_data
	           && get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY
	           && NM_FLAGS_ANY (property_info->property_typ_data->typ_flags,   NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC
	                                                                         | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX
	                                                                         | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT)) {
		format_numeric_hex = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX);
		format_numeric = format_numeric && NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC);
		format_text = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT);
	} else if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) {
		/* by default, output in format "%u (%s)" (with hex for flags and l10n). */
		format_numeric = TRUE;
		format_numeric_hex_unknown = TRUE;
		format_text = TRUE;
		format_text_l10n = TRUE;
	} else {
		/* by default, output only numeric (with hex for flags). */
		format_numeric = TRUE;
		format_numeric_hex_unknown = TRUE;
	}

	nm_assert (format_text || format_numeric);

	gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name);

	g_value_init (&gval, gtype_prop);

	g_object_get_property (G_OBJECT (setting), property_info->property_name, &gval);

	if (   gtype_prop == G_TYPE_INT
	    || (    G_TYPE_IS_CLASSED (gtype_prop)
	        &&  G_IS_ENUM_CLASS ((gtype_prop_class ?: (gtype_prop_class = g_type_class_ref (gtype_prop)))))) {
		if (gtype_prop == G_TYPE_INT) {
			if (!has_gtype)
				g_return_val_if_reached (NULL);
			v = g_value_get_int (&gval);
		} else
		    v = g_value_get_enum (&gval);
	} else if (   gtype_prop == G_TYPE_UINT
	           || (   G_TYPE_IS_CLASSED (gtype_prop)
	               && G_IS_FLAGS_CLASS ((gtype_prop_class ?: (gtype_prop_class = g_type_class_ref (gtype_prop)))))) {
		if (gtype_prop == G_TYPE_UINT) {
			if (!has_gtype)
				g_return_val_if_reached (NULL);
			v = g_value_get_uint (&gval);
		} else
		    v = g_value_get_flags (&gval);
	} else
		g_return_val_if_reached (NULL);

	if (!has_gtype) {
		gtype = gtype_prop;
		gtype_class = g_steal_pointer (&gtype_prop_class);
	}

	nm_assert (({
		nm_auto_unref_gtypeclass GTypeClass *t = NULL;

		(   G_TYPE_IS_CLASSED (gtype)
		 && (t = g_type_class_ref (gtype))
		 && (G_IS_ENUM_CLASS (t) || G_IS_FLAGS_CLASS (t)));
	}));

	if (format_numeric && !format_text) {
		s =    format_numeric_hex
		    || (   format_numeric_hex_unknown
		        && !G_IS_ENUM_CLASS (gtype_class ?: (gtype_class = g_type_class_ref (gtype))))
		    ? g_strdup_printf ("0x%"G_GINT64_FORMAT, v)
		    : g_strdup_printf ("%"G_GINT64_FORMAT, v);
		RETURN_STR_TO_FREE (g_steal_pointer (&s));
	}

	/* the gobject_enum.value_infos are currently ignored for the getter. They
	 * only declare additional aliases for the setter. */

	s = nm_utils_enum_to_str (gtype, (int) v);

	if (!format_numeric)
		RETURN_STR_TO_FREE (g_steal_pointer (&s));

	if (   format_numeric_hex
	    || (   format_numeric_hex_unknown
	        && !G_IS_ENUM_CLASS (gtype_class ?: (gtype_class = g_type_class_ref (gtype)))))
		nm_sprintf_buf (s_numeric, "0x%"G_GINT64_FORMAT, v);
	else
		nm_sprintf_buf (s_numeric, "%"G_GINT64_FORMAT, v);

	if (nm_streq0 (s, s_numeric))
		RETURN_STR_TO_FREE (g_steal_pointer (&s));

	if (format_text_l10n)
		RETURN_STR_TO_FREE (g_strdup_printf (_("%s (%s)"), s_numeric, s));
	else
		RETURN_STR_TO_FREE (g_strdup_printf ("%s (%s)", s_numeric, s));
}

/*****************************************************************************/

static gboolean
_set_fcn_gobject_string (ARGS_SET_FCN)
{
	gs_free char *to_free = NULL;

	if (property_info->property_typ_data) {
		if (property_info->property_typ_data->subtype.gobject_string.validate_fcn) {
			value = property_info->property_typ_data->subtype.gobject_string.validate_fcn (value, &to_free, error);
			if (!value)
				return FALSE;
		} else if (property_info->property_typ_data->values_static) {
			value = nmc_string_is_valid (value,
			                             (const char **) property_info->property_typ_data->values_static,
			                             error);
			if (!value)
				return FALSE;
		}
	}
	g_object_set (setting, property_info->property_name, value, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_bool (ARGS_SET_FCN)
{
	gboolean val_bool;

	if (!nmc_string_to_bool (value, &val_bool, error))
		return FALSE;

	g_object_set (setting, property_info->property_name, val_bool, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_int (ARGS_SET_FCN)
{
	int errsv;
	const GParamSpec *pspec;
	nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
	gint64 v = 0;
	gboolean has_minmax = FALSE;
	gint64 min = G_MININT64;
	gint64 max = G_MAXINT64;
	guint base = 10;
	const NMMetaUtilsIntValueInfo *value_infos = NULL;
	gboolean has_value = FALSE;

	if (property_info->property_typ_data) {

		if (   value
		    && (value_infos = property_info->property_typ_data->subtype.gobject_int.value_infos)) {
			gs_free char *vv_stripped = NULL;
			const char *vv = nm_str_skip_leading_spaces (value);

			if (vv[0] && g_ascii_isspace (vv[strlen (vv) - 1])) {
				vv_stripped = g_strstrip (g_strdup (vv));
				vv = vv_stripped;
			}

			for (; value_infos->nick; value_infos++) {
				if (nm_streq (value_infos->nick, vv)) {
					v = value_infos->value;
					has_value = TRUE;
					break;
				}
			}
		}

		if (property_info->property_typ_data->subtype.gobject_int.base > 0)
			base = property_info->property_typ_data->subtype.gobject_int.base;
		if (   property_info->property_typ_data->subtype.gobject_int.min
		    || property_info->property_typ_data->subtype.gobject_int.max) {
			min = property_info->property_typ_data->subtype.gobject_int.min;
			max = property_info->property_typ_data->subtype.gobject_int.max;
			has_minmax = TRUE;
		}
	}

	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), property_info->property_name);
	if (!G_IS_PARAM_SPEC (pspec))
		g_return_val_if_reached (FALSE);
	switch (pspec->value_type) {
	case G_TYPE_INT:
		if (!has_minmax) {
			const GParamSpecInt *p = (GParamSpecInt *) pspec;

			min = p->minimum;
			max = p->maximum;
		}
		break;
	case G_TYPE_UINT:
		if (!has_minmax) {
			const GParamSpecUInt *p = (GParamSpecUInt *) pspec;

			min = p->minimum;
			max = p->maximum;
		}
		break;
	case G_TYPE_INT64:
		if (!has_minmax) {
			const GParamSpecInt64 *p = (GParamSpecInt64 *) pspec;

			min = p->minimum;
			max = p->maximum;
		}
		break;
	default:
		g_return_val_if_reached (FALSE);
	}

	if (!has_value) {
		v = _nm_utils_ascii_str_to_int64 (value, base, min, max, 0);

		if ((errsv = errno) != 0) {
			if (errsv == ERANGE) {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("'%s' is out of range [%lli, %lli]"),
				             value,
				             (long long) min,
				             (long long) max);
			} else {
				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
				             _("'%s' is not a valid number"), value);
			}
			return FALSE;
		}
	}

	g_value_init (&gval, pspec->value_type);
	switch (pspec->value_type) {
	case G_TYPE_INT:
		g_value_set_int (&gval, v);
		break;
	case G_TYPE_UINT:
		g_value_set_uint (&gval, v);
		break;
	case G_TYPE_INT64:
		g_value_set_int64 (&gval, v);
		break;
	default:
		nm_assert_not_reached ();
		break;
	}

	/* Validate the number according to the property spec */
	if (!nm_g_object_set_property (G_OBJECT (setting),
	                               property_info->property_name,
	                               &gval,
	                               error))
		g_return_val_if_reached (FALSE);

	return TRUE;
}

static gboolean
_set_fcn_gobject_mtu (ARGS_SET_FCN)
{
	nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
	const GParamSpec *pspec;
	gint64 v;

	if (nm_streq0 (value, "auto"))
		value = "0";

	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)),
	                                      property_info->property_name);
	if (!pspec || pspec->value_type != G_TYPE_UINT)
		g_return_val_if_reached (FALSE);

	v = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
	if (v < 0) {
		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
		             _("'%s' is out of range [0, %u]"), value, (unsigned) G_MAXUINT32);
		return FALSE;
	}

	g_value_init (&gval, pspec->value_type);
	g_value_set_uint (&gval, v);

	if (!nm_g_object_set_property (G_OBJECT (setting),
	                               property_info->property_name,
	                               &gval,
	                               error))
		g_return_val_if_reached (FALSE);

	return TRUE;
}

static gboolean
_set_fcn_gobject_mac (ARGS_SET_FCN)
{
	NMMetaPropertyTypeMacMode mode;
	gboolean valid;

	if (property_info->property_typ_data)
		mode = property_info->property_typ_data->subtype.mac.mode;
	else
		mode = NM_META_PROPERTY_TYPE_MAC_MODE_DEFAULT;


	if (mode == NM_META_PROPERTY_TYPE_MAC_MODE_INFINIBAND)
		valid = nm_utils_hwaddr_valid (value, INFINIBAND_ALEN);
	else {
		valid =    nm_utils_hwaddr_valid (value, ETH_ALEN)
		        || (   mode == NM_META_PROPERTY_TYPE_MAC_MODE_CLONED
		            && NM_CLONED_MAC_IS_SPECIAL (value));
	}

	if (!valid) {
		g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), value);
		return FALSE;
	}

	g_object_set (setting, property_info->property_name, value, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_secret_flags (ARGS_SET_FCN)
{
	char **strv = NULL, **iter;
	unsigned long flags = 0, val_int;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	for (iter = strv; iter && *iter; iter++) {
		if (!nmc_string_to_uint (*iter, TRUE, 0, ALL_SECRET_FLAGS, &val_int)) {
			g_set_error (error, 1, 0, _("'%s' is not a valid flag number; use <0-%d>"),
			             *iter, ALL_SECRET_FLAGS);
			g_strfreev (strv);
			return FALSE;
		}
		flags += val_int;
	}
	g_strfreev (strv);

	/* Validate the flags number */
	if (flags > ALL_SECRET_FLAGS) {
		flags = ALL_SECRET_FLAGS;
		_env_warn_fcn (environment, environment_user_data,
		               NM_META_ENV_WARN_LEVEL_WARN,
		               N_("'%s' sum is higher than all flags => all flags set"),
		               value);
	}

	g_object_set (setting, property_info->property_name, (guint) flags, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_enum (ARGS_SET_FCN)
{
	GType gtype = 0;
	GType gtype_prop;
	gboolean has_gtype = FALSE;
	nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
	nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL;
	gboolean is_flags;
	int v;

	if (property_info->property_typ_data) {
		if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) {
			gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype ();
			has_gtype = TRUE;
		}
	}

	gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name);

	if (   has_gtype
	    && NM_IN_SET (gtype_prop,
	                  G_TYPE_INT,
	                  G_TYPE_UINT)
	    && G_TYPE_IS_CLASSED (gtype)
	    && (gtype_class = g_type_class_ref (gtype))
	    && (   (is_flags = G_IS_FLAGS_CLASS (gtype_class))
	        || G_IS_ENUM_CLASS (gtype_class))) {
		/* valid */
	} else if (   !has_gtype
	           && G_TYPE_IS_CLASSED (gtype_prop)
	           && (gtype_class = g_type_class_ref (gtype_prop))
	           && (   (is_flags = G_IS_FLAGS_CLASS (gtype_class))
	               || G_IS_ENUM_CLASS (gtype_class))) {
		gtype = gtype_prop;
	} else
		g_return_val_if_reached (FALSE);

	if (!_nm_utils_enum_from_str_full (gtype, value, &v, NULL,
	                                   property_info->property_typ_data
	                                       ? property_info->property_typ_data->subtype.gobject_enum.value_infos
	                                       : NULL))
		goto fail;

	if (   property_info->property_typ_data
	    && property_info->property_typ_data->subtype.gobject_enum.pre_set_notify) {
		property_info->property_typ_data->subtype.gobject_enum.pre_set_notify (property_info,
		                                                                       environment,
		                                                                       environment_user_data,
		                                                                       setting,
		                                                                       v);
	}

	g_value_init (&gval, gtype_prop);
	if (gtype_prop == G_TYPE_INT)
		g_value_set_int (&gval, v);
	else if (gtype_prop == G_TYPE_UINT)
		g_value_set_uint (&gval, v);
	else if (G_IS_ENUM_CLASS (gtype_class))
		g_value_set_enum (&gval, v);
	else if (G_IS_FLAGS_CLASS (gtype_class))
		g_value_set_flags (&gval, v);
	else
		g_return_val_if_reached (FALSE);

	if (!nm_g_object_set_property (G_OBJECT (setting), property_info->property_name, &gval, NULL))
		goto fail;

	return TRUE;

fail:
	if (error) {
		gs_free const char **valid_all = NULL;
		gs_free const char *valid_str = NULL;
		gboolean has_minmax = FALSE;
		int min = G_MININT;
		int max = G_MAXINT;

		if (property_info->property_typ_data) {
			if (   property_info->property_typ_data->subtype.gobject_enum.min
			    || property_info->property_typ_data->subtype.gobject_enum.max) {
				min = property_info->property_typ_data->subtype.gobject_enum.min;
				max = property_info->property_typ_data->subtype.gobject_enum.max;
				has_minmax = TRUE;
			}
		}

		if (!has_minmax && is_flags) {
			min = 0;
			max = (gint) G_MAXUINT;
		}

		valid_all = nm_utils_enum_get_values (gtype, min, max);
		valid_str = g_strjoinv (",", (char **) valid_all);
		if (is_flags) {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("invalid option '%s', use a combination of [%s]"),
			             value,
			             valid_str);
		} else {
			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
			             _("invalid option '%s', use one of [%s]"),
			             value,
			             valid_str);
		}
	}
	return FALSE;
}

/*****************************************************************************/

static const char *const*
_values_fcn_gobject_enum (ARGS_VALUES_FCN)
{
	GType gtype = 0;
	gboolean has_gtype = FALSE;
	gboolean has_minmax = FALSE;
	int min = G_MININT;
	int max = G_MAXINT;
	char **v, **w;

	if (property_info->property_typ_data) {
		if (   property_info->property_typ_data->subtype.gobject_enum.min
		    || property_info->property_typ_data->subtype.gobject_enum.max) {
			min = property_info->property_typ_data->subtype.gobject_enum.min;
			max = property_info->property_typ_data->subtype.gobject_enum.max;
			has_minmax = TRUE;
		}
		if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) {
			gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype ();
			has_gtype = TRUE;
		}
	}

	if (!has_gtype) {
		gtype = _gtype_property_get_gtype (property_info->setting_info->general->get_setting_gtype (),
		                                   property_info->property_name);
	}

	if (   !has_minmax
	    && G_TYPE_IS_CLASSED (gtype)) {
		nm_auto_unref_gtypeclass GTypeClass *class = NULL;

		class = g_type_class_ref (gtype);
		if (G_IS_FLAGS_CLASS (class)) {
			min = 0;
			max = (gint) G_MAXUINT;
		}
	}

	/* the gobject_enum.value_infos are currently ignored for the list of
	 * values. They only declare additional (hidden) aliases for the setter. */

	v = (char **) nm_utils_enum_get_values (gtype, min, max);
	if (v) {
		for (w = v; *w; w++)
			*w = g_strdup (*w);
	}
	return (const char *const*) (*out_to_free = v);
}

/*****************************************************************************/

static const char *const*
_complete_fcn_gobject_bool (ARGS_COMPLETE_FCN)
{
	static const char *const v[] = {
		"true",
		"false",
		"on",
		"off",
		"1",
		"0",
		"yes",
		"no",
		NULL,
	};

	if (!text || !text[0])
		return &v[6];
	return v;
}

static const char *const*
_complete_fcn_gobject_devices (ARGS_COMPLETE_FCN)
{
	NMDevice *const*devices = NULL;
	guint i, j;
	guint len = 0;
	char **ifnames;

	if (   environment
	    && environment->get_nm_devices) {
		devices = environment->get_nm_devices (environment,
		                                       environment_user_data,
		                                       &len);
	}

	if (len == 0)
		return NULL;

	ifnames = g_new (char *, len + 1);
	for (i = 0, j = 0; i < len; i++) {
		const char *ifname;

		nm_assert (NM_IS_DEVICE (devices[i]));

		ifname = nm_device_get_iface (devices[i]);
		if (ifname)
			ifnames[j++] = g_strdup (ifname);
	}
	ifnames[j++] = NULL;

	*out_to_free = ifnames;
	return (const char *const*) ifnames;
}

/*****************************************************************************/

static char *
wep_key_type_to_string (NMWepKeyType type)
{
	switch (type) {
	case NM_WEP_KEY_TYPE_KEY:
		return g_strdup_printf (_("%d (key)"), type);
	case NM_WEP_KEY_TYPE_PASSPHRASE:
		return g_strdup_printf (_("%d (passphrase)"), type);
	case NM_WEP_KEY_TYPE_UNKNOWN:
	default:
		return g_strdup_printf (_("%d (unknown)"), type);
	}
}

static char *
bytes_to_string (GBytes *bytes)
{
	const guint8 *data;
	gsize len;
	GString *cert = NULL;
	int i;

	if (!bytes)
		return NULL;
	data = g_bytes_get_data (bytes, &len);

	cert = g_string_new (NULL);
	for (i = 0; i < len; i++)
		g_string_append_printf (cert, "%02X", data[i]);

	return g_string_free (cert, FALSE);
}

static char *
vlan_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type)
{
	GString *flag_str;

	if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY)
		return g_strdup_printf ("%u", flags);

	if (flags == 0)
		return g_strdup (_("0 (NONE)"));

	flag_str = g_string_new (NULL);
	g_string_printf (flag_str, "%d (", flags);

	if (flags & NM_VLAN_FLAG_REORDER_HEADERS)
		g_string_append (flag_str, _("REORDER_HEADERS, "));
	if (flags & NM_VLAN_FLAG_GVRP)
		g_string_append (flag_str, _("GVRP, "));
	if (flags & NM_VLAN_FLAG_LOOSE_BINDING)
		g_string_append (flag_str, _("LOOSE_BINDING, "));
	if (flags & NM_VLAN_FLAG_MVRP)
		g_string_append (flag_str, _("MVRP, "));

	if (flag_str->str[flag_str->len-1] == '(')
		g_string_append (flag_str, _("unknown"));
	else
		g_string_truncate (flag_str, flag_str->len-2);  /* chop off trailing ', ' */

	g_string_append_c (flag_str, ')');

	return g_string_free (flag_str, FALSE);
}

static char *
vlan_priorities_to_string (NMSettingVlan *s_vlan, NMVlanPriorityMap map)
{
	GString *priorities;
	int i;

	priorities = g_string_new (NULL);
	for (i = 0; i < nm_setting_vlan_get_num_priorities (s_vlan, map); i++) {
		guint32 from, to;

		if (nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to))
			g_string_append_printf (priorities, "%d:%d,", from, to);
	}
	if (priorities->len)
		g_string_truncate (priorities, priorities->len-1);  /* chop off trailing ',' */

	return g_string_free (priorities, FALSE);
}

static char *
ip6_privacy_to_string (NMSettingIP6ConfigPrivacy ip6_privacy, NMMetaAccessorGetType get_type)
{
	if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY)
		return g_strdup_printf ("%d", ip6_privacy);

	switch (ip6_privacy) {
	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
		return g_strdup_printf (_("%d (disabled)"), ip6_privacy);
	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
		return g_strdup_printf (_("%d (enabled, prefer public IP)"), ip6_privacy);
	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
		return g_strdup_printf (_("%d (enabled, prefer temporary IP)"), ip6_privacy);
	default:
		return g_strdup_printf (_("%d (unknown)"), ip6_privacy);
	}
}

static char *
secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type)
{
	GString *flag_str;

	if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY)
		return g_strdup_printf ("%u", flags);

	if (flags == 0)
		return g_strdup (_("0 (none)"));

	flag_str = g_string_new (NULL);
	g_string_printf (flag_str, "%u (", flags);

	if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)
		g_string_append (flag_str, _("agent-owned, "));
	if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
		g_string_append (flag_str, _("not saved, "));
	if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
		g_string_append (flag_str, _("not required, "));

	if (flag_str->str[flag_str->len-1] == '(')
		g_string_append (flag_str, _("unknown"));
	else
		g_string_truncate (flag_str, flag_str->len-2);  /* chop off trailing ', ' */

	g_string_append_c (flag_str, ')');

	return g_string_free (flag_str, FALSE);
}

static void
vpn_data_item (const char *key, const char *value, gpointer user_data)
{
	GString *ret_str = (GString *) user_data;

	if (ret_str->len != 0)
		g_string_append (ret_str, ", ");

	g_string_append_printf (ret_str, "%s = %s", key, value);
}

#define DEFINE_SETTER_STR_LIST_MULTI(def_func, s_macro, set_func) \
	static gboolean \
	def_func (NMSetting *setting, \
	          const char *prop, \
	          const char *value, \
	          const char **valid_strv, \
	          GError **error) \
	{ \
		char **strv = NULL, **iter; \
		const char *item; \
		g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \
		strv = nmc_strsplit_set (value, " \t,", 0); \
		for (iter = strv; iter && *iter; iter++) { \
			if (!(item = nmc_string_is_valid (g_strstrip (*iter), valid_strv, error))) { \
				g_strfreev (strv); \
				return FALSE; \
			} \
			set_func (s_macro (setting), item); \
		} \
		g_strfreev (strv); \
		return TRUE; \
	}

#define DEFINE_SETTER_OPTIONS(def_func, s_macro, s_type, add_func, valid_func1, valid_func2) \
	static gboolean \
	def_func (ARGS_SET_FCN) \
	{ \
		char **strv = NULL, **iter; \
		const char **(*valid_func1_p) (s_type *) = valid_func1; \
		const char * (*valid_func2_p) (const char *, const char *, GError **) = valid_func2; \
		const char *opt_name, *opt_val; \
		\
		g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \
		\
		strv = nmc_strsplit_set (value, ",", 0); \
		for (iter = strv; iter && *iter; iter++) { \
			char *left = g_strstrip (*iter); \
			char *right = strchr (left, '='); \
			if (!right) { \
				g_set_error (error, 1, 0, _("'%s' is not valid; use <option>=<value>"), *iter); \
				g_strfreev (strv); \
				return FALSE; \
			} \
			*right++ = '\0'; \
			\
			if (valid_func1_p) { \
				const char **valid_options = valid_func1_p (s_macro (setting)); \
				if (!(opt_name = nmc_string_is_valid (g_strstrip (left), valid_options, error))) { \
					g_strfreev (strv); \
					return FALSE; \
				} \
			} else \
				opt_name = g_strstrip (left);\
			\
			opt_val = g_strstrip (right); \
			if (valid_func2_p) { \
				if (!(opt_val = valid_func2_p ((const char *) left, (const char *) opt_val, error))) { \
					g_strfreev (strv); \
					return FALSE; \
				}\
			}\
			add_func (s_macro (setting), opt_name, opt_val); \
		} \
		g_strfreev (strv); \
		return TRUE; \
	}

#define DEFINE_REMOVER_INDEX_OR_VALUE(def_func, s_macro, num_func, rem_func_idx, rem_func_val) \
	static gboolean \
	def_func (ARGS_REMOVE_FCN) \
	{ \
		guint32 num; \
		if (value) { \
			gboolean ret; \
			char *value_stripped = g_strstrip (g_strdup (value)); \
			ret = rem_func_val (s_macro (setting), value_stripped, error); \
			g_free (value_stripped); \
			return ret; \
		} \
		num = num_func (s_macro (setting)); \
		if (num == 0) { \
			g_set_error_literal (error, 1, 0, _("no item to remove")); \
			return FALSE; \
		} \
		if (idx >= num) { \
			g_set_error (error, 1, 0, _("index '%d' is not in range <0-%d>"), idx, num - 1); \
			return FALSE; \
		} \
		rem_func_idx (s_macro (setting), idx); \
		return TRUE; \
	}

#define DEFINE_REMOVER_OPTION(def_func, s_macro, rem_func) \
	static gboolean \
	def_func (ARGS_REMOVE_FCN) \
	{ \
		gboolean success = FALSE; \
		if (value && *value) { \
			success = rem_func (s_macro (setting), value); \
			if (!success) \
				g_set_error (error, 1, 0, _("invalid option '%s'"), value); \
		} else \
			g_set_error_literal (error, 1, 0, _("missing option")); \
		return success; \
	}

#define DEFINE_ALLOWED_VAL_FUNC(def_func, valid_values) \
	static const char *const* \
	def_func (NMSetting *setting, const char *prop) \
	{ \
		return valid_values; \
	}

#define DEFINE_SETTER_MAC_BLACKLIST(def_func, s_macro, add_func) \
	static gboolean \
	def_func (ARGS_SET_FCN) \
	{ \
		guint8 buf[32]; \
		char **list = NULL, **iter; \
		GSList *macaddr_blacklist = NULL; \
		\
		g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \
		\
		list = nmc_strsplit_set (value, " \t,", 0); \
		for (iter = list; iter && *iter; iter++) { \
			if (!nm_utils_hwaddr_aton (*iter, buf, ETH_ALEN)) { \
				g_set_error (error, 1, 0, _("'%s' is not a valid MAC"), *iter); \
				g_strfreev (list); \
				g_slist_free (macaddr_blacklist); \
				return FALSE; \
			} \
		} \
		\
		for (iter = list; iter && *iter; iter++) \
			add_func (s_macro (setting), *iter); \
		\
		g_strfreev (list); \
		return TRUE; \
	}


static gboolean
verify_string_list (char **strv,
                    const char *prop,
                    gboolean (*validate_func) (const char *),
                    GError **error)
{
	char **iter;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	for (iter = strv; iter && *iter; iter++) {
		if (**iter == '\0')
			continue;
		if (validate_func) {
			if (!validate_func (*iter)) {
				g_set_error (error, 1, 0, _("'%s' is not valid"),
				             *iter);
				return FALSE;
			}
		}
	}
	return TRUE;
}

static char *
flag_values_to_string (GFlagsValue *array, guint n)
{
	GString *str;
	guint i;

	str = g_string_new (NULL);
	for (i = 0; i < n; i++)
		g_string_append_printf (str, "%u, ", array[i].value);
	if (str->len)
		g_string_truncate (str, str->len-2);  /* chop off trailing ', ' */
	return g_string_free (str, FALSE);
}

static gboolean
validate_flags (NMSetting *setting, const char* prop, guint val, GError **error)
{
	GParamSpec *pspec;
	GValue value = G_VALUE_INIT;
	gboolean success = TRUE;

	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop);
	g_assert (G_IS_PARAM_SPEC (pspec));

	g_value_init (&value, pspec->value_type);
	g_value_set_flags (&value, val);

	if (g_param_value_validate (pspec, &value)) {
		GParamSpecFlags *pspec_flags = (GParamSpecFlags *) pspec;
		char *flag_values = flag_values_to_string (pspec_flags->flags_class->values,
		                                           pspec_flags->flags_class->n_values);
		g_set_error (error, 1, 0, _("'%u' flags are not valid; use combination of %s"),
		             val, flag_values);
		g_free (flag_values);
		success = FALSE;
	}
	g_value_unset (&value);
	return success;
}

static gboolean
check_and_set_string (NMSetting *setting,
                      const char *prop,
                      const char *val,
                      const char **valid_strv,
                      GError **error)
{
	const char *checked_val;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	checked_val = nmc_string_is_valid (val, valid_strv, error);
	if (!checked_val)
		return FALSE;

	g_object_set (setting, prop, checked_val, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_flags (ARGS_SET_FCN)
{
	unsigned long val_int;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!nmc_string_to_uint (value, TRUE, 0, G_MAXUINT, &val_int)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), value);
		return FALSE;
	}

	/* Validate the flags according to the property spec */
	if (!validate_flags (setting, property_info->property_name, (guint) val_int, error))
		return FALSE;

	g_object_set (setting, property_info->property_name, (guint) val_int, NULL);
	return TRUE;
}

static gboolean
_set_fcn_gobject_ssid (ARGS_SET_FCN)
{
	GBytes *ssid;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (strlen (value) > 32) {
		g_set_error (error, 1, 0, _("'%s' is not valid"), value);
		return FALSE;
	}

	ssid = g_bytes_new (value, strlen (value));
	g_object_set (setting, property_info->property_name, ssid, NULL);
	g_bytes_unref (ssid);
	return TRUE;
}

static gboolean
_set_fcn_gobject_ifname (ARGS_SET_FCN)
{
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!nm_utils_is_valid_iface_name (value, error))
		return FALSE;
	g_object_set (setting, property_info->property_name, value, NULL);
	return TRUE;
}

static gboolean
_set_fcn_vpn_service_type (ARGS_SET_FCN)
{
	gs_free char *service_name = NULL;

	service_name = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), value);
	g_object_set (setting, property_info->property_name, service_name ? : value, NULL);
	return TRUE;
}

static const char *const*
_complete_fcn_vpn_service_type (ARGS_COMPLETE_FCN)
{
	gsize i, j;
	char **values;

	values = nm_vpn_plugin_info_list_get_service_types (nm_vpn_get_plugin_infos (), FALSE, TRUE);
	if (!values)
		return NULL;

	if (!text || !*text) {
		/* If the prompt text is empty or contains no '.',
		 * filter out full names. */
		for (i = 0, j = 0; values[i]; i++) {
			if (strchr (values[i], '.')) {
				g_free (values[i]);
				continue;
			}

			if (i != j)
				values[j] = values[i];
			j++;
		}
		if (j)
			values[j++] = NULL;
		else {
			g_free (values);
			values = NULL;
		}
	}
	return (const char *const*) (*out_to_free = values);
}

static gboolean
nmc_util_is_domain (const char *domain)
{
	//FIXME: implement
	return TRUE;
}

static gboolean
nmc_property_set_bytes (NMSetting *setting, const char *prop, const char *value, GError **error)
{
	gs_free char *val_strip = NULL;
	gs_strfreev char **strv = NULL;
	const char *delimiters = " \t,";
	char **iter;
	GBytes *bytes;
	GByteArray *array = NULL;
	gboolean success = TRUE;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	val_strip = g_strstrip (g_strdup (value));

	/* First try hex string in the format of AAbbCCDd */
	bytes = nm_utils_hexstr2bin (val_strip);
	if (bytes)
		goto done;

	/* Otherwise, consider the following format: AA b 0xCc D */
	strv = nmc_strsplit_set (val_strip, delimiters, 0);
	array = g_byte_array_sized_new (g_strv_length (strv));
	for (iter = strv; iter && *iter; iter++) {
		int v;
		guint8 v8;

		v = _nm_utils_ascii_str_to_int64 (*iter, 16, 0, 255, -1);
		if (v == -1) {
			g_set_error (error, 1, 0, _("'%s' is not a valid hex character"), *iter);
			g_byte_array_free (array, TRUE);
			success = FALSE;
			goto done;
		}
		v8 = v;
		g_byte_array_append (array, &v8, 1);
	}
	bytes = g_byte_array_free_to_bytes (array);

done:
	if (success)
		g_object_set (setting, prop, bytes, NULL);

	if (bytes)
		g_bytes_unref (bytes);

	return success;
}

/*****************************************************************************/

static gconstpointer
_get_fcn_802_1x_ca_cert (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *ca_cert_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_ca_cert_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		ca_cert_str = bytes_to_string (nm_setting_802_1x_get_ca_cert_blob (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		ca_cert_str = g_strdup (nm_setting_802_1x_get_ca_cert_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		ca_cert_str = g_strdup (nm_setting_802_1x_get_ca_cert_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (ca_cert_str);
}

static gconstpointer
_get_fcn_802_1x_client_cert (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *cert_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_client_cert_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		if (NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS))
			cert_str = bytes_to_string (nm_setting_802_1x_get_client_cert_blob (s_8021X));
		else
			return _get_text_hidden (get_type);
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		cert_str = g_strdup (nm_setting_802_1x_get_client_cert_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		cert_str = g_strdup (nm_setting_802_1x_get_client_cert_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (cert_str);
}

static gconstpointer
_get_fcn_802_1x_phase2_ca_cert (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *phase2_ca_cert_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		phase2_ca_cert_str = bytes_to_string (nm_setting_802_1x_get_phase2_ca_cert_blob (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		phase2_ca_cert_str = g_strdup (nm_setting_802_1x_get_phase2_ca_cert_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		phase2_ca_cert_str = g_strdup (nm_setting_802_1x_get_phase2_ca_cert_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (phase2_ca_cert_str);
}

static gconstpointer
_get_fcn_802_1x_phase2_client_cert (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *cert_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_phase2_client_cert_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		if (NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS))
			cert_str = bytes_to_string (nm_setting_802_1x_get_phase2_client_cert_blob (s_8021X));
		else
			return _get_text_hidden (get_type);
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		cert_str = g_strdup (nm_setting_802_1x_get_phase2_client_cert_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		cert_str = g_strdup (nm_setting_802_1x_get_phase2_client_cert_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (cert_str);
}

static gconstpointer
_get_fcn_802_1x_password_raw (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);

	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (bytes_to_string (nm_setting_802_1x_get_password_raw (s_8021X)));
}

static gconstpointer
_get_fcn_802_1x_private_key (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *key_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_private_key_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		if (NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS))
			key_str = bytes_to_string (nm_setting_802_1x_get_private_key_blob (s_8021X));
		else
			return _get_text_hidden (get_type);
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		key_str = g_strdup (nm_setting_802_1x_get_private_key_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		key_str = g_strdup (nm_setting_802_1x_get_private_key_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (key_str);
}

static gconstpointer
_get_fcn_802_1x_phase2_private_key (ARGS_GET_FCN)
{
	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
	char *key_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021X)) {
	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
		if (NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS))
			key_str = bytes_to_string (nm_setting_802_1x_get_phase2_private_key_blob (s_8021X));
		else
			return _get_text_hidden (get_type);
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PATH:
		key_str = g_strdup (nm_setting_802_1x_get_phase2_private_key_path (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
		key_str = g_strdup (nm_setting_802_1x_get_phase2_private_key_uri (s_8021X));
		break;
	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
		break;
	}

	RETURN_STR_TO_FREE (key_str);
}

#define DEFINE_SETTER_STR_LIST(def_func, set_func) \
	static gboolean \
	def_func (ARGS_SET_FCN) \
	{ \
		char **strv = NULL; \
		guint i = 0; \
		\
		g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \
		\
		strv = nmc_strsplit_set (value, " \t,", 0); \
		while (strv && strv[i]) \
			set_func (NM_SETTING_802_1X (setting), strv[i++]); \
		g_strfreev (strv); \
		return TRUE; \
	}

#define DEFINE_SETTER_CERT(def_func, set_func) \
	static gboolean \
	def_func (ARGS_SET_FCN) \
	{ \
		char *val_strip = g_strstrip (g_strdup (value)); \
		char *p = val_strip; \
		NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_PATH; \
		gboolean success; \
		\
		if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11)) == 0) \
			scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11; \
		else if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0) \
			p += NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); \
		\
		success = set_func (NM_SETTING_802_1X (setting), p, scheme, NULL, error); \
		g_free (val_strip); \
		return success; \
	}

#define DEFINE_SETTER_PRIV_KEY(def_func, pwd_func, set_func) \
	static gboolean \
	def_func (ARGS_SET_FCN) \
	{ \
		char **strv = NULL; \
		char *val_strip = g_strstrip (g_strdup (value)); \
		char *p = val_strip; \
		const char *path, *password; \
		gs_free char *password_free = NULL; \
		NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_PATH; \
		gboolean success; \
		\
		if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11)) == 0) \
			scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11; \
		else if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0) \
			p += NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); \
		\
		strv = nmc_strsplit_set (p, " \t,", 2); \
		path = strv[0]; \
		if (g_strv_length (strv) == 2) \
			password = strv[1]; \
		else \
			password = password_free = g_strdup (pwd_func (NM_SETTING_802_1X (setting))); \
		success = set_func (NM_SETTING_802_1X (setting), path, password, scheme, NULL, error); \
		g_free (val_strip); \
		g_strfreev (strv); \
		return success; \
	}

DEFINE_SETTER_STR_LIST_MULTI (check_and_add_eap_method,
                              NM_SETTING_802_1X,
                              nm_setting_802_1x_add_eap_method)

static gboolean
_set_fcn_802_1x_eap (ARGS_SET_FCN)
{
	return check_and_add_eap_method (setting,
	                                 property_info->property_name,
	                                 value,
	                                 (const char **) property_info->property_typ_data->values_static,
	                                 error);
}

static gboolean
_validate_and_remove_eap_method (NMSetting8021x *setting,
                                 const char *eap,
                                 GError **error)
{
	gboolean ret;

	ret = nm_setting_802_1x_remove_eap_method_by_value (setting, eap);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain EAP method '%s'"), eap);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_802_1x_eap,
                               NM_SETTING_802_1X,
                               nm_setting_802_1x_get_num_eap_methods,
                               nm_setting_802_1x_remove_eap_method,
                               _validate_and_remove_eap_method)

DEFINE_SETTER_CERT (_set_fcn_802_1x_ca_cert, nm_setting_802_1x_set_ca_cert)

DEFINE_SETTER_STR_LIST (_set_fcn_802_1x_altsubject_matches, nm_setting_802_1x_add_altsubject_match)

static gboolean
_validate_and_remove_altsubject_match (NMSetting8021x *setting,
                                       const char *altsubject_match,
                                       GError **error)
{
	gboolean ret;

	ret = nm_setting_802_1x_remove_altsubject_match_by_value (setting, altsubject_match);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain alternative subject match '%s'"),
		             altsubject_match);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_802_1x_altsubject_matches,
                               NM_SETTING_802_1X,
                               nm_setting_802_1x_get_num_altsubject_matches,
                               nm_setting_802_1x_remove_altsubject_match,
                               _validate_and_remove_altsubject_match)

DEFINE_SETTER_CERT (_set_fcn_802_1x_client_cert, nm_setting_802_1x_set_client_cert)

DEFINE_SETTER_CERT (_set_fcn_802_1x_phase2_ca_cert, nm_setting_802_1x_set_phase2_ca_cert)

DEFINE_SETTER_STR_LIST (_set_fcn_802_1x_phase2_altsubject_matches, nm_setting_802_1x_add_phase2_altsubject_match)

static gboolean
_validate_and_remove_phase2_altsubject_match (NMSetting8021x *setting,
                                              const char *phase2_altsubject_match,
                                              GError **error)
{
	gboolean ret;

	ret = nm_setting_802_1x_remove_phase2_altsubject_match_by_value (setting, phase2_altsubject_match);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain \"phase2\" alternative subject match '%s'"),
		             phase2_altsubject_match);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_802_1x_phase2_altsubject_matches,
                               NM_SETTING_802_1X,
                               nm_setting_802_1x_get_num_phase2_altsubject_matches,
                               nm_setting_802_1x_remove_phase2_altsubject_match,
                               _validate_and_remove_phase2_altsubject_match)

DEFINE_SETTER_CERT (_set_fcn_802_1x_phase2_client_cert, nm_setting_802_1x_set_phase2_client_cert)

DEFINE_SETTER_PRIV_KEY (_set_fcn_802_1x_private_key,
                        nm_setting_802_1x_get_private_key_password,
                        nm_setting_802_1x_set_private_key)

DEFINE_SETTER_PRIV_KEY (_set_fcn_802_1x_phase2_private_key,
                        nm_setting_802_1x_get_phase2_private_key_password,
                        nm_setting_802_1x_set_phase2_private_key)

static gboolean
_set_fcn_802_1x_password_raw (ARGS_SET_FCN)
{
	return nmc_property_set_bytes (setting, property_info->property_name, value, error);
}

static gconstpointer
_get_fcn_bond_options (ARGS_GET_FCN)
{
	NMSettingBond *s_bond = NM_SETTING_BOND (setting);
	GString *bond_options_s;
	int i;

	RETURN_UNSUPPORTED_GET_TYPE ();

	bond_options_s = g_string_new (NULL);
	for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) {
		const char *key, *value;
		gs_free char *tmp_value = NULL;
		char *p;

		nm_setting_bond_get_option (s_bond, i, &key, &value);

		if (nm_streq0 (key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
			value = tmp_value = g_strdup (value);
			for (p = tmp_value; p && *p; p++) {
				if (*p == ',')
					*p = ' ';
			}
		}

		g_string_append_printf (bond_options_s, "%s=%s,", key, value);
	}
	g_string_truncate (bond_options_s, bond_options_s->len-1);  /* chop off trailing ',' */

	RETURN_STR_TO_FREE (g_string_free (bond_options_s, FALSE));
}

/*  example: miimon=100,mode=balance-rr, updelay=5 */
static gboolean
_validate_and_remove_bond_option (NMSettingBond *setting, const char *option)
{
	const char *opt;
	const char **valid_options;

	valid_options = nm_setting_bond_get_valid_options (setting);
	opt = nmc_string_is_valid (option, valid_options, NULL);

	if (opt)
		return nm_setting_bond_remove_option (setting, opt);
	else
		return FALSE;
}

static const char *
_validate_bond_option_value (const char *option, const char *value, GError **error)
{
	if (!g_strcmp0 (option, NM_SETTING_BOND_OPTION_MODE))
		return nmc_bond_validate_mode (value, error);

	return value;
}

static gboolean
_bond_add_option (NMSettingBond *setting,
                  const char *name,
                  const char *value)
{
	gs_free char *tmp_value = NULL;
	char *p;

	if (nm_streq0 (name, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
		value = tmp_value = g_strdup (value);
		for (p = tmp_value; p && *p; p++)
			if (*p == ' ')
				*p = ',';
	}

	return nm_setting_bond_add_option (setting, name, value);
}

DEFINE_SETTER_OPTIONS (_set_fcn_bond_options,
                       NM_SETTING_BOND,
                       NMSettingBond,
                       _bond_add_option,
                       nm_setting_bond_get_valid_options,
                       _validate_bond_option_value)
DEFINE_REMOVER_OPTION (_remove_fcn_bond_options,
                       NM_SETTING_BOND,
                       _validate_and_remove_bond_option)

static const char *
_describe_fcn_bond_options (ARGS_DESCRIBE_FCN)
{
	gs_free char *options_str = NULL;
	const char **valid_options;
	char *s;

	valid_options = nm_setting_bond_get_valid_options (NULL);
	options_str = g_strjoinv (", ", (char **) valid_options);

	s = g_strdup_printf (_("Enter a list of bonding options formatted as:\n"
	                       "  option = <value>, option = <value>,... \n"
	                       "Valid options are: %s\n"
	                       "'mode' can be provided as a name or a number:\n"
	                       "balance-rr    = 0\n"
	                       "active-backup = 1\n"
	                       "balance-xor   = 2\n"
	                       "broadcast     = 3\n"
	                       "802.3ad       = 4\n"
	                       "balance-tlb   = 5\n"
	                       "balance-alb   = 6\n\n"
	                       "Example: mode=2,miimon=120\n"), options_str);
	return (*out_to_free = s);
}

static const char *const*
_values_fcn_bond_options (ARGS_VALUES_FCN)
{
	return nm_setting_bond_get_valid_options (NULL);
}

static gconstpointer
_get_fcn_connection_permissions (ARGS_GET_FCN)
{
	NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting);
	GString *perm = NULL;
	const char *perm_item;
	const char *perm_type;
	int i;

	RETURN_UNSUPPORTED_GET_TYPE ();

	perm = g_string_new (NULL);
	for (i = 0; i < nm_setting_connection_get_num_permissions (s_con); i++) {
		if (nm_setting_connection_get_permission (s_con, i, &perm_type, &perm_item, NULL))
			g_string_append_printf (perm, "%s:%s,", perm_type, perm_item);
	}
	if (perm->len > 0) {
		g_string_truncate (perm, perm->len-1); /* remove trailing , */
		RETURN_STR_TO_FREE (g_string_free (perm, FALSE));
	}

	/* No value from get_permission */
	g_string_free (perm, TRUE);
	return NULL;
}

static gboolean
_set_fcn_connection_type (ARGS_SET_FCN)
{
	gs_free char *uuid = NULL;

	if (nm_setting_connection_get_uuid (NM_SETTING_CONNECTION (setting))) {
		/* Don't allow setting type unless the connection is brand new.
		 * Just because it's a bad idea and the user wouldn't probably want that.
		 * No technical reason, really.
		 * Also, using uuid to see if the connection is brand new is a bit
		 * hacky: we can not see if the type is already set, because
		 * nmc_setting_set_property() is called only after the property
		 * we're setting (type) has been removed. */
		g_set_error (error, 1, 0, _("Can not change the connection type"));
		return FALSE;
	}

	uuid = nm_utils_uuid_generate ();
	g_object_set (G_OBJECT (setting),
	              NM_SETTING_CONNECTION_UUID, uuid,
	              NULL);

	g_object_set (G_OBJECT (setting), property_info->property_name, value, NULL);
	return TRUE;
}

static const char *const*
_complete_fcn_connection_type (ARGS_COMPLETE_FCN)
{
	guint i, j;
	char **result;
	gsize text_len;

	result = g_new (char *, _NM_META_SETTING_TYPE_NUM * 2 + 1);

	text_len = text ? strlen (text) : 0;

	for (i = 0, j = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
		const NMMetaSettingInfoEditor *setting_info = &nm_meta_setting_infos_editor[i];
		const char *v;

		if (!setting_info->valid_parts)
			continue;

		v = setting_info->alias;
		if (v) {
			if (!text || strncmp (text, v, text_len) == 0)
				result[j++] = g_strdup (v);
		}
		if (!text || !*text || !v) {
			v = setting_info->general->setting_name;
			if (!text || strncmp (text, v, text_len) == 0)
				result[j++] = g_strdup (v);
		}
	}
	if (j)
		result[j++] = NULL;
	else {
		g_free (result);
		result = NULL;
	}

	return (const char *const*) (*out_to_free = result);
}

/* define from libnm-core/nm-setting-connection.c */
#define PERM_USER_PREFIX  "user:"

static gboolean
permissions_valid (const char *perm)
{
	if (!perm || perm[0] == '\0')
		return FALSE;

	if (strncmp (perm, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0) {
		if (   strlen (perm) <= strlen (PERM_USER_PREFIX)
		    || strchr (perm + strlen (PERM_USER_PREFIX), ':'))
			return  FALSE;
	} else {
		if (strchr (perm, ':'))
			return  FALSE;
	}

	return TRUE;
}

static gboolean
_set_fcn_connection_permissions (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	if (!verify_string_list (strv, property_info->property_name, permissions_valid, error)) {
		g_strfreev (strv);
		return FALSE;
	}

	for (i = 0; strv && strv[i]; i++) {
		const char *user;

		if (strncmp (strv[i], PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0)
			user = strv[i]+strlen (PERM_USER_PREFIX);
		else
			user = strv[i];

		nm_setting_connection_add_permission (NM_SETTING_CONNECTION (setting), "user", user, NULL);
	}

	return TRUE;
}

static gboolean
_validate_and_remove_connection_permission (NMSettingConnection *setting,
                                            const char *perm,
                                            GError **error)
{
	gboolean ret;

	ret = nm_setting_connection_remove_permission_by_value (setting, "user", perm, NULL);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain permission '%s'"), perm);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_connection_permissions,
                               NM_SETTING_CONNECTION,
                               nm_setting_connection_get_num_permissions,
                               nm_setting_connection_remove_permission,
                               _validate_and_remove_connection_permission)

static gboolean
_set_fcn_connection_master (ARGS_SET_FCN)
{
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!value)
		;
	else if (!*value)
		value = NULL;
	else if (   !nm_utils_is_valid_iface_name (value, NULL)
	         && !nm_utils_is_uuid (value)) {
		g_set_error (error, 1, 0,
		             _("'%s' is not valid master; use ifname or connection UUID"),
		             value);
		return FALSE;
	}
	g_object_set (setting, property_info->property_name, value, NULL);
	return TRUE;
}

static const char *const*
_complete_fcn_connection_master (ARGS_COMPLETE_FCN)
{
	NMRemoteConnection *const*connections = NULL;
	guint len = 0;
	guint i, j;
	char **result;
	NMSettingConnection *s_con;
	const char *expected_type = NULL;
	gsize text_len;

	if (   environment
	    && environment->get_nm_connections) {
		connections = environment->get_nm_connections (environment,
		                                               environment_user_data,
		                                               &len);
	}
	if (!len)
		return NULL;

	if (   (!text || !*text)
	    && operation_context
	    && operation_context->connection) {
		/* if we have no text yet, initially only complete for matching
		 * slave-type. */
		s_con = nm_connection_get_setting_connection (operation_context->connection);
		if (s_con)
			expected_type = nm_setting_connection_get_slave_type (s_con);
	}

	text_len = text ? strlen (text) : 0;

	result = g_new (char *, (2 * len) + 1);
	for (i = 0, j = 0; i < len; i++) {
		const char *v;

		s_con = nm_connection_get_setting_connection (NM_CONNECTION (connections[i]));
		if (!s_con)
			continue;

		if (   expected_type
		    && !nm_streq0 (nm_setting_connection_get_connection_type (s_con),
		                   expected_type))
			continue;

		if (text && text[0]) {
			/* if we have text, also complete for the UUID. */
			v = nm_setting_connection_get_uuid (s_con);
			if (v && (!text || strncmp (text, v, text_len) == 0))
				result[j++] = g_strdup (v);
		}

		v = nm_setting_connection_get_interface_name (s_con);
		if (v && (!text || strncmp (text, v, text_len) == 0))
			result[j++] = g_strdup (v);
	}
	if (j)
		result[j++] = NULL;
	else {
		g_free (result);
		result = NULL;
	}

	return (const char *const*) (*out_to_free = result);
}

static gboolean
_set_fcn_connection_secondaries (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	char **iter;

	strv = nmc_strsplit_set (value, " \t,", 0);
	if (strv) {
		for (iter = strv; *iter; iter++) {
			if (**iter)
				nm_setting_connection_add_secondary (NM_SETTING_CONNECTION (setting), *iter);
		}
	}
	return TRUE;
}

static gboolean
_validate_and_remove_connection_secondary (NMSettingConnection *setting,
                                           const char *secondary_uuid,
                                           GError **error)
{
	gboolean ret;

	if (!nm_utils_is_uuid (secondary_uuid)) {
		g_set_error (error, 1, 0,
		             _("the value '%s' is not a valid UUID"), secondary_uuid);
		return FALSE;
	}

	ret = nm_setting_connection_remove_secondary_by_value (setting, secondary_uuid);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain UUID '%s'"), secondary_uuid);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_connection_secondaries,
                               NM_SETTING_CONNECTION,
                               nm_setting_connection_get_num_secondaries,
                               nm_setting_connection_remove_secondary,
                               _validate_and_remove_connection_secondary)

static gconstpointer
_get_fcn_connection_metered (ARGS_GET_FCN)
{
	NMSettingConnection *s_conn = NM_SETTING_CONNECTION (setting);
	const char *s;

	RETURN_UNSUPPORTED_GET_TYPE ();

	switch (nm_setting_connection_get_metered (s_conn)) {
	case NM_METERED_YES:
		s = N_("yes");
		break;
	case NM_METERED_NO:
		s = N_("no");
		break;
	case NM_METERED_UNKNOWN:
	default:
		s = N_("unknown");
		break;
	}
	if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
		return _(s);
	return s;
}

static gboolean
_set_fcn_connection_metered (ARGS_SET_FCN)
{
	NMMetered metered;
	NMCTriStateValue ts_val;

	if (!nmc_string_to_tristate (value, &ts_val, error))
		return FALSE;

	switch (ts_val) {
	case NMC_TRI_STATE_YES:
		metered = NM_METERED_YES;
		break;
	case NMC_TRI_STATE_NO:
		metered = NM_METERED_NO;
		break;
	case NMC_TRI_STATE_UNKNOWN:
		metered = NM_METERED_UNKNOWN;
		break;
	default:
		g_assert_not_reached();
	}

	g_object_set (setting, property_info->property_name, metered, NULL);
	return TRUE;
}

static char *
dcb_flags_to_string (NMSettingDcbFlags flags)
{
	GString *flag_str;

	if (flags == 0)
		return g_strdup (_("0 (disabled)"));

	flag_str = g_string_new (NULL);
	g_string_printf (flag_str, "%d (", flags);

	if (flags & NM_SETTING_DCB_FLAG_ENABLE)
		g_string_append (flag_str, _("enabled, "));
	if (flags & NM_SETTING_DCB_FLAG_ADVERTISE)
		g_string_append (flag_str, _("advertise, "));
	if (flags & NM_SETTING_DCB_FLAG_WILLING)
		g_string_append (flag_str, _("willing, "));

	if (flag_str->str[flag_str->len-1] == '(')
		g_string_append (flag_str, _("unknown"));
	else
		g_string_truncate (flag_str, flag_str->len-2);  /* chop off trailing ', ' */

	g_string_append_c (flag_str, ')');

	return g_string_free (flag_str, FALSE);
}

#define DEFINE_DCB_FLAGS_GETTER(func_name, property_name) \
	static gconstpointer \
	func_name (ARGS_GET_FCN) \
	{ \
		guint v; \
		GValue val = G_VALUE_INIT; \
		\
		RETURN_UNSUPPORTED_GET_TYPE (); \
		g_value_init (&val, G_TYPE_UINT); \
		g_object_get_property (G_OBJECT (setting), property_name, &val); \
		v = g_value_get_uint (&val); \
		g_value_unset (&val); \
		RETURN_STR_TO_FREE (dcb_flags_to_string (v)); \
	}

#define DEFINE_DCB_BOOL_GETTER(func_name, getter_func_name) \
	static gconstpointer \
	func_name (ARGS_GET_FCN) \
	{ \
		NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \
		GString *str; \
		guint i; \
		\
		RETURN_UNSUPPORTED_GET_TYPE (); \
		\
		str = g_string_new (NULL); \
		for (i = 0; i < 8; i++) { \
			if (getter_func_name (s_dcb,  i)) \
				g_string_append_c (str, '1'); \
			else \
				g_string_append_c (str, '0'); \
			if (i < 7) \
				g_string_append_c (str, ','); \
		} \
		\
		RETURN_STR_TO_FREE (g_string_free (str, FALSE)); \
	}

#define DEFINE_DCB_UINT_GETTER(func_name, getter_func_name) \
	static gconstpointer \
	func_name (ARGS_GET_FCN) \
	{ \
		NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \
		GString *str; \
		guint i; \
		\
		RETURN_UNSUPPORTED_GET_TYPE (); \
		\
		str = g_string_new (NULL); \
		for (i = 0; i < 8; i++) { \
			g_string_append_printf (str, "%u", getter_func_name (s_dcb, i)); \
			if (i < 7) \
				g_string_append_c (str, ','); \
		} \
		\
		RETURN_STR_TO_FREE (g_string_free (str, FALSE)); \
	}

DEFINE_DCB_FLAGS_GETTER (_get_fcn_dcb_app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_FLAGS)
DEFINE_DCB_FLAGS_GETTER (_get_fcn_dcb_app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_FLAGS)
DEFINE_DCB_FLAGS_GETTER (_get_fcn_dcb_app_fip_flags, NM_SETTING_DCB_APP_FIP_FLAGS)

DEFINE_DCB_FLAGS_GETTER (_get_fcn_dcb_priority_flow_control_flags, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS)
DEFINE_DCB_BOOL_GETTER (_get_fcn_dcb_priority_flow_control, nm_setting_dcb_get_priority_flow_control)

DEFINE_DCB_FLAGS_GETTER (_get_fcn_dcb_priority_group_flags, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS)
DEFINE_DCB_UINT_GETTER (_get_fcn_dcb_priority_group_id, nm_setting_dcb_get_priority_group_id)
DEFINE_DCB_UINT_GETTER (_get_fcn_dcb_priority_group_bandwidth, nm_setting_dcb_get_priority_group_bandwidth)
DEFINE_DCB_UINT_GETTER (_get_fcn_dcb_priority_bandwidth, nm_setting_dcb_get_priority_bandwidth)
DEFINE_DCB_BOOL_GETTER (_get_fcn_dcb_priority_strict, nm_setting_dcb_get_priority_strict_bandwidth)
DEFINE_DCB_UINT_GETTER (_get_fcn_dcb_priority_traffic_class, nm_setting_dcb_get_priority_traffic_class)

#define DCB_ALL_FLAGS (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING)

static gboolean
_set_fcn_dcb_flags (ARGS_SET_FCN)
{
	char **strv = NULL, **iter;
	NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
	long int t;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	/* Check for overall hex numeric value */
	t = _nm_utils_ascii_str_to_int64 (value, 0, 0, DCB_ALL_FLAGS, -1);
	if (t != -1)
		flags = (guint) t;
	else {
		/* Check for individual flag numbers */
		strv = nmc_strsplit_set (value, " \t,", 0);
		for (iter = strv; iter && *iter; iter++) {
			t = _nm_utils_ascii_str_to_int64 (*iter, 0, 0, DCB_ALL_FLAGS, -1);

			if (   g_ascii_strcasecmp (*iter, "enable") == 0
			    || g_ascii_strcasecmp (*iter, "enabled") == 0
			    || t == NM_SETTING_DCB_FLAG_ENABLE)
				flags |= NM_SETTING_DCB_FLAG_ENABLE;
			else if (   g_ascii_strcasecmp (*iter, "advertise") == 0
				 || t == NM_SETTING_DCB_FLAG_ADVERTISE)
				flags |= NM_SETTING_DCB_FLAG_ADVERTISE;
			else if (   g_ascii_strcasecmp (*iter, "willing") == 0
				 || t == NM_SETTING_DCB_FLAG_WILLING)
				flags |= NM_SETTING_DCB_FLAG_WILLING;
			else if (   g_ascii_strcasecmp (*iter, "disable") == 0
				 || g_ascii_strcasecmp (*iter, "disabled") == 0
				 || t == 0) {
				/* pass */
			} else {
				g_set_error (error, 1, 0, _("'%s' is not a valid DCB flag"), *iter);
				return FALSE;
			}
		}
		g_strfreev (strv);
	}

	/* Validate the flags according to the property spec */
	if (!validate_flags (setting, property_info->property_name, (guint) flags, error))
		return FALSE;

	g_object_set (setting, property_info->property_name, (guint) flags, NULL);
	return TRUE;
}

static gboolean
dcb_parse_uint_array (const char *val,
                      guint max,
                      guint other,
                      guint *out_array,
                      GError **error)
{
	gs_strfreev char **items = NULL;
	char **iter;
	gsize i;

	nm_assert (out_array);

	items = g_strsplit_set (val, ",", -1);
	if (g_strv_length (items) != 8) {
		g_set_error_literal (error, 1, 0, _("must contain 8 comma-separated numbers"));
		return FALSE;
	}

	i = 0;
	for (iter = items; *iter; iter++) {
		gint64 num;

		*iter = g_strstrip (*iter);

		num = _nm_utils_ascii_str_to_int64 (*iter, 10, 0, other ? other : max, -1);

		/* If number is greater than 'max' it must equal 'other' */
		if (   num == -1
		    || (other && (num > max) && (num != other))) {
			if (other) {
				g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive) or %u"),
				             *iter, max, other);
			} else {
				g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive)"),
				             *iter, max);
			}
			return FALSE;
		}
		nm_assert (i < 8);
		out_array[i++] = (guint) num;
	}

	return TRUE;
}

static void
dcb_check_feature_enabled (const NMMetaEnvironment *environment, gpointer *environment_user_data, NMSettingDcb *s_dcb, const char *flags_prop)
{
	NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;

	g_object_get (s_dcb, flags_prop, &flags, NULL);
	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
		_env_warn_fcn (environment, environment_user_data,
		               NM_META_ENV_WARN_LEVEL_WARN,
		               N_("changes will have no effect until '%s' includes 1 (enabled)"),
		               flags_prop);
	}
}

static gboolean
_set_fcn_dcb_priority_flow_control (ARGS_SET_FCN)
{
	guint i = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 1, 0, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_flow_control (NM_SETTING_DCB (setting), i, !!nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_dcb_priority_group_id (ARGS_SET_FCN)
{
	guint i = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 7, 15, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_group_id (NM_SETTING_DCB (setting), i, nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_dcb_priority_group_bandwidth (ARGS_SET_FCN)
{
	guint i = 0, sum = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 100, 0, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		sum += nums[i];
	if (sum != 100) {
		g_set_error_literal (error, 1, 0, _("bandwidth percentages must total 100%%"));
		return FALSE;
	}

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_group_bandwidth (NM_SETTING_DCB (setting), i, nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_dcb_priority_bandwidth (ARGS_SET_FCN)
{
	guint i = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 100, 0, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_bandwidth (NM_SETTING_DCB (setting), i, nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_dcb_priority_strict (ARGS_SET_FCN)
{
	guint i = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 1, 0, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_strict_bandwidth (NM_SETTING_DCB (setting), i, !!nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_dcb_priority_traffic_class (ARGS_SET_FCN)
{
	guint i = 0;
	guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!dcb_parse_uint_array (value, 7, 0, nums, error))
		return FALSE;

	for (i = 0; i < 8; i++)
		nm_setting_dcb_set_priority_traffic_class (NM_SETTING_DCB (setting), i, nums[i]);

	dcb_check_feature_enabled (environment, environment_user_data, NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS);
	return TRUE;
}

static gboolean
_set_fcn_gsm_sim_operator_id (ARGS_SET_FCN)
{
	const char *p = value;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (strlen (value) != 5 && strlen (value) != 6) {
		g_set_error_literal (error, 1, 0, _("SIM operator ID must be a 5 or 6 number MCCMNC code"));
		return FALSE;
	}

	while (p && *p) {
		if (!g_ascii_isdigit (*p++)) {
			g_set_error_literal (error, 1, 0, _("SIM operator ID must be a 5 or 6 number MCCMNC code"));
			return FALSE;
		}
	}
	g_object_set (G_OBJECT (setting),
	              NM_SETTING_GSM_SIM_OPERATOR_ID,
	              value,
	              NULL);
	return TRUE;
}

static gboolean
_set_fcn_infiniband_p_key (ARGS_SET_FCN)
{
	const gint64 INVALID = G_MININT64;
	gint64 p_key;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (nm_streq (value, "default"))
		p_key = -1;
	else {
		p_key = _nm_utils_ascii_str_to_int64 (value, 0, -1, G_MAXUINT16, INVALID);
		if (p_key == INVALID) {
			g_set_error (error, 1, 0, _("'%s' is not a valid IBoIP P_Key"), value);
			return FALSE;
		}
	}

	g_object_set (setting, property_info->property_name, (int) p_key, NULL);
	return TRUE;
}


static gconstpointer
_get_fcn_infiniband_p_key (ARGS_GET_FCN)
{
	NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting);
	int p_key;

	RETURN_UNSUPPORTED_GET_TYPE ();

	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
	if (p_key == -1) {
		if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY)
			return "default";
		else
			return _("default");
	} else
		RETURN_STR_TO_FREE (g_strdup_printf ("0x%04x", p_key));
}

static gconstpointer
_get_fcn_ip_config_addresses (ARGS_GET_FCN)
{
	NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
	GString *printable;
	guint num_addresses, i;
	NMIPAddress *addr;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_addresses = NM_MIN ((guint) G_MAXINT, nm_setting_ip_config_get_num_addresses (s_ip));
	for (i = 0; i < num_addresses; i++) {
		addr = nm_setting_ip_config_get_address (s_ip, i);

		if (printable->len > 0)
			g_string_append (printable, ", ");

		g_string_append_printf (printable, "%s/%u",
		                        nm_ip_address_get_address (addr),
		                        nm_ip_address_get_prefix (addr));
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static gconstpointer
_get_fcn_ip_config_routes (ARGS_GET_FCN)
{
	NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
	GString *printable;
	guint num_routes, i;
	NMIPRoute *route;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_routes = NM_MIN ((guint) G_MAXINT, nm_setting_ip_config_get_num_routes (s_ip));
	for (i = 0; i < num_routes; i++) {
		gs_free char *attr_str = NULL;
		gs_strfreev char **attr_names = NULL;
		gs_unref_hashtable GHashTable *hash = g_hash_table_new (nm_str_hash, g_str_equal);
		int j;

		route = nm_setting_ip_config_get_route (s_ip, i);

		attr_names = nm_ip_route_get_attribute_names (route);
		for (j = 0; attr_names && attr_names[j]; j++) {
			g_hash_table_insert (hash, attr_names[j],
			                     nm_ip_route_get_attribute (route, attr_names[j]));
		}

		attr_str = nm_utils_format_variant_attributes (hash, ' ', '=');

		if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) {
			if (printable->len > 0)
				g_string_append (printable, ", ");

			g_string_append_printf (printable, "%s/%u",
			                        nm_ip_route_get_dest (route),
			                        nm_ip_route_get_prefix (route));

			if (nm_ip_route_get_next_hop (route))
				g_string_append_printf (printable, " %s", nm_ip_route_get_next_hop (route));
			if (nm_ip_route_get_metric (route) != -1)
				g_string_append_printf (printable, " %u", (guint32) nm_ip_route_get_metric (route));
			if (attr_str)
				g_string_append_printf (printable, " %s", attr_str);
		} else {

			if (printable->len > 0)
				g_string_append (printable, "; ");

			g_string_append (printable, "{ ");

			g_string_append_printf (printable, "ip = %s/%u",
			                        nm_ip_route_get_dest (route),
			                        nm_ip_route_get_prefix (route));

			if (nm_ip_route_get_next_hop (route)) {
				g_string_append_printf (printable, ", nh = %s",
				                        nm_ip_route_get_next_hop (route));
			}

			if (nm_ip_route_get_metric (route) != -1)
				g_string_append_printf (printable, ", mt = %u", (guint32) nm_ip_route_get_metric (route));
			if (attr_str)
				g_string_append_printf (printable, " %s", attr_str);

			g_string_append (printable, " }");
		}
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static const char *ipv4_valid_methods[] = {
	NM_SETTING_IP4_CONFIG_METHOD_AUTO,
	NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
	NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
	NM_SETTING_IP4_CONFIG_METHOD_SHARED,
	NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
	NULL
};

static gboolean
_set_fcn_ip4_config_method (ARGS_SET_FCN)
{
	/* Silently accept "static" and convert to "manual" */
	if (value && strlen (value) > 1 && matches (value, "static"))
		value = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;

	return check_and_set_string (setting, property_info->property_name, value, ipv4_valid_methods, error);
}

static gboolean
_set_fcn_ip4_config_dns (ARGS_SET_FCN)
{
	char **strv = NULL, **iter, *addr;
	guint32 ip4_addr;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	for (iter = strv; iter && *iter; iter++) {
		addr = g_strstrip (*iter);
		if (inet_pton (AF_INET, addr, &ip4_addr) < 1) {
			g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), addr);
			g_strfreev (strv);
			return FALSE;
		}
		nm_setting_ip_config_add_dns (NM_SETTING_IP_CONFIG (setting), addr);
	}
	g_strfreev (strv);
	return TRUE;
}

static gboolean
_validate_and_remove_ipv4_dns (NMSettingIPConfig *setting,
                               const char *dns,
                               GError **error)
{
	guint32 ip4_addr;
	gboolean ret;

	if (inet_pton (AF_INET, dns, &ip4_addr) < 1) {
		g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), dns);
		return FALSE;
	}

	ret = nm_setting_ip_config_remove_dns_by_value (setting, dns);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv4_config_dns,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns,
                               nm_setting_ip_config_remove_dns,
                               _validate_and_remove_ipv4_dns)

static gboolean
_set_fcn_ip4_config_dns_search (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	if (!verify_string_list (strv, property_info->property_name, nmc_util_is_domain, error)) {
		g_strfreev (strv);
		return FALSE;
	}

	while (strv && strv[i])
		nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (setting), strv[i++]);
	g_strfreev (strv);

	return TRUE;
}

static gboolean
_validate_and_remove_ipv4_dns_search (NMSettingIPConfig *setting,
                                      const char *dns_search,
                                      GError **error)
{
	gboolean ret;

	ret = nm_setting_ip_config_remove_dns_search_by_value (setting, dns_search);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain DNS search domain '%s'"),
		             dns_search);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv4_config_dns_search,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns_searches,
                               nm_setting_ip_config_remove_dns_search,
                               _validate_and_remove_ipv4_dns_search)

static gboolean
_set_fcn_ip4_config_dns_options (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	nm_setting_ip_config_clear_dns_options (NM_SETTING_IP_CONFIG (setting), TRUE);
	strv = nmc_strsplit_set (value, " \t,", 0);
	while (strv && strv[i])
		nm_setting_ip_config_add_dns_option (NM_SETTING_IP_CONFIG (setting), strv[i++]);
	g_strfreev (strv);

	return TRUE;
}

static gboolean
_validate_and_remove_ipv4_dns_option (NMSettingIPConfig *setting,
                                      const char *dns_option,
                                      GError **error)
{
	gboolean ret;

	ret = nm_setting_ip_config_remove_dns_option_by_value (setting, dns_option);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain DNS option '%s'"),
		             dns_option);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv4_config_dns_options,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns_options,
                               nm_setting_ip_config_remove_dns_option,
                               _validate_and_remove_ipv4_dns_option)

static gboolean
_set_fcn_ip4_config_addresses (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMIPAddress *ip4addr;

	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		ip4addr = _parse_ip_address (AF_INET, *iter, error);
		if (!ip4addr)
			return FALSE;
		nm_setting_ip_config_add_address (NM_SETTING_IP_CONFIG (setting), ip4addr);
		nm_ip_address_unref (ip4addr);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_ipv4_address (NMSettingIPConfig *setting,
                                   const char *address,
                                   GError **error)
{
	NMIPAddress *ip4addr;
	gboolean ret;

	ip4addr = _parse_ip_address (AF_INET, address, error);
	if (!ip4addr)
		return FALSE;

	ret = nm_setting_ip_config_remove_address_by_value (setting, ip4addr);
	if (!ret) {
		g_set_error (error, 1, 0,
		             _("the property doesn't contain IP address '%s'"), address);
	}
	nm_ip_address_unref (ip4addr);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv4_config_addresses,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_addresses,
                               nm_setting_ip_config_remove_address,
                               _validate_and_remove_ipv4_address)

static gboolean
_set_fcn_ip4_config_gateway (ARGS_SET_FCN)
{
	gs_free char *addr = NULL;

	addr = g_strstrip (g_strdup (value));

	if (!nm_utils_ipaddr_valid (AF_INET, addr)) {
		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
	                 _("invalid gateway address '%s'"), value);
		return FALSE;
	}
	g_object_set (setting, property_info->property_name, addr, NULL);
	return TRUE;
}

static gboolean
_set_fcn_ip4_config_routes (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMIPRoute *ip4route;

	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		ip4route = _parse_ip_route (AF_INET, *iter, error);
		if (!ip4route)
			return FALSE;
		nm_setting_ip_config_add_route (NM_SETTING_IP_CONFIG (setting), ip4route);
		nm_ip_route_unref (ip4route);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_ipv4_route (NMSettingIPConfig *setting,
                                 const char *route,
                                 GError **error)
{
	NMIPRoute *ip4route;
	gboolean ret;

	ip4route = _parse_ip_route (AF_INET, route, error);
	if (!ip4route)
		return FALSE;

	ret = nm_setting_ip_config_remove_route_by_value (setting, ip4route);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route);
	nm_ip_route_unref (ip4route);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv4_config_routes,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_routes,
                               nm_setting_ip_config_remove_route,
                               _validate_and_remove_ipv4_route)

static gconstpointer
_get_fcn_ip6_config_ip6_privacy (ARGS_GET_FCN)
{
	NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (setting);
	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (ip6_privacy_to_string (nm_setting_ip6_config_get_ip6_privacy (s_ip6), get_type));
}

static const char *ipv6_valid_methods[] = {
	NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
	NM_SETTING_IP6_CONFIG_METHOD_AUTO,
	NM_SETTING_IP6_CONFIG_METHOD_DHCP,
	NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
	NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
	NM_SETTING_IP6_CONFIG_METHOD_SHARED,
	NULL
};

static gboolean
_set_fcn_ip6_config_method (ARGS_SET_FCN)
{
	/* Silently accept "static" and convert to "manual" */
	if (value && strlen (value) > 1 && matches (value, "static"))
		value = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;

	return check_and_set_string (setting, property_info->property_name, value, ipv6_valid_methods, error);
}

static gboolean
_set_fcn_ip6_config_dns (ARGS_SET_FCN)
{
	char **strv = NULL, **iter, *addr;
	struct in6_addr ip6_addr;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	for (iter = strv; iter && *iter; iter++) {
		addr = g_strstrip (*iter);
		if (inet_pton (AF_INET6, addr, &ip6_addr) < 1) {
			g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), addr);
			g_strfreev (strv);
			return FALSE;
		}
		nm_setting_ip_config_add_dns (NM_SETTING_IP_CONFIG (setting), addr);
	}
	g_strfreev (strv);
	return TRUE;
}

static gboolean
_validate_and_remove_ipv6_dns (NMSettingIPConfig *setting,
                               const char *dns,
                               GError **error)
{
	struct in6_addr ip6_addr;
	gboolean ret;

	if (inet_pton (AF_INET6, dns, &ip6_addr) < 1) {
		g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), dns);
		return FALSE;
	}

	ret = nm_setting_ip_config_remove_dns_by_value (setting, dns);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_dns,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns,
                               nm_setting_ip_config_remove_dns,
                               _validate_and_remove_ipv6_dns)

static gboolean
_set_fcn_ip6_config_dns_search (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = nmc_strsplit_set (value, " \t,", 0);
	if (!verify_string_list (strv, property_info->property_name, nmc_util_is_domain, error)) {
		g_strfreev (strv);
		return FALSE;
	}

	while (strv && strv[i])
		nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (setting), strv[i++]);
	g_strfreev (strv);

	return TRUE;
}

static gboolean
_validate_and_remove_ipv6_dns_search (NMSettingIPConfig *setting,
                                      const char *dns_search,
                                      GError **error)
{
	gboolean ret;

	ret = nm_setting_ip_config_remove_dns_search_by_value (setting, dns_search);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain DNS search domain '%s'"),
		             dns_search);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_dns_search,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns_searches,
                               nm_setting_ip_config_remove_dns_search,
                               _validate_and_remove_ipv6_dns_search)

static gboolean
_set_fcn_ip6_config_dns_options (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	nm_setting_ip_config_clear_dns_options (NM_SETTING_IP_CONFIG (setting), TRUE);
	strv = nmc_strsplit_set (value, " \t,", 0);
	while (strv && strv[i])
		nm_setting_ip_config_add_dns_option (NM_SETTING_IP_CONFIG (setting), strv[i++]);
	g_strfreev (strv);

	return TRUE;
}

static gboolean
_validate_and_remove_ipv6_dns_option (NMSettingIPConfig *setting,
                                      const char *dns_option,
                                      GError **error)
{
	gboolean ret;

	ret = nm_setting_ip_config_remove_dns_option_by_value (setting, dns_option);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain DNS option '%s'"),
		             dns_option);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_dns_options,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_dns_options,
                               nm_setting_ip_config_remove_dns_option,
                               _validate_and_remove_ipv6_dns_option)

static gboolean
_dns_options_is_default (NMSettingIPConfig *setting)
{
	return    nm_setting_ip_config_has_dns_options (setting)
	       && !nm_setting_ip_config_get_num_dns_options (setting);
}

static gboolean
_set_fcn_ip6_config_addresses (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMIPAddress *ip6addr;

	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		ip6addr = _parse_ip_address (AF_INET6, *iter, error);
		if (!ip6addr)
			return FALSE;
		nm_setting_ip_config_add_address (NM_SETTING_IP_CONFIG (setting), ip6addr);
		nm_ip_address_unref (ip6addr);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_ipv6_address (NMSettingIPConfig *setting,
                                   const char *address,
                                   GError **error)
{
	NMIPAddress *ip6addr;
	gboolean ret;

	ip6addr = _parse_ip_address (AF_INET6, address, error);
	if (!ip6addr)
		return FALSE;

	ret = nm_setting_ip_config_remove_address_by_value (setting, ip6addr);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain IP address '%s'"), address);
	nm_ip_address_unref (ip6addr);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_addresses,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_addresses,
                               nm_setting_ip_config_remove_address,
                               _validate_and_remove_ipv6_address)

static gboolean
_set_fcn_ip6_config_gateway (ARGS_SET_FCN)
{
	gs_free char *addr = NULL;

	addr = g_strstrip (g_strdup (value));

	if (!nm_utils_ipaddr_valid (AF_INET6, addr)) {
		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
		             _("invalid gateway address '%s'"),
		             addr);
		return FALSE;
	}

	g_object_set (setting, property_info->property_name, addr, NULL);
	return TRUE;
}

static gboolean
_set_fcn_ip6_config_routes (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMIPRoute *ip6route;

	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		ip6route = _parse_ip_route (AF_INET6, *iter, error);
		if (!ip6route)
			return FALSE;
		nm_setting_ip_config_add_route (NM_SETTING_IP_CONFIG (setting), ip6route);
		nm_ip_route_unref (ip6route);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_ipv6_route (NMSettingIPConfig *setting,
                                 const char *route,
                                 GError **error)
{
	NMIPRoute *ip6route;
	gboolean ret;

	ip6route = _parse_ip_route (AF_INET6, route, error);
	if (!ip6route)
		return FALSE;

	ret = nm_setting_ip_config_remove_route_by_value (setting, ip6route);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route);
	nm_ip_route_unref (ip6route);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_routes,
                               NM_SETTING_IP_CONFIG,
                               nm_setting_ip_config_get_num_routes,
                               nm_setting_ip_config_remove_route,
                               _validate_and_remove_ipv6_route)

static gboolean
_set_fcn_ip6_config_ip6_privacy (ARGS_SET_FCN)
{
	unsigned long val_int;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!nmc_string_to_uint (value, FALSE, 0, 0, &val_int)) {
		g_set_error (error, 1, 0, _("'%s' is not a number"), value);
		return FALSE;
	}

	if (   val_int != NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED
	    && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR
	    && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) {
		g_set_error (error, 1, 0, _("'%s' is not valid; use 0, 1, or 2"), value);
		return FALSE;
	}

	g_object_set (setting, property_info->property_name, val_int, NULL);
	return TRUE;
}

static gconstpointer
_get_fcn_olpc_mesh_ssid (ARGS_GET_FCN)
{
	NMSettingOlpcMesh *s_olpc_mesh = NM_SETTING_OLPC_MESH (setting);
	GBytes *ssid;
	char *ssid_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	ssid = nm_setting_olpc_mesh_get_ssid (s_olpc_mesh);
	if (ssid) {
		ssid_str = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
		                                  g_bytes_get_size (ssid));
	}

	RETURN_STR_TO_FREE (ssid_str);
}

static gboolean
_set_fcn_olpc_mesh_channel (ARGS_SET_FCN)
{
	unsigned long chan_int;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!nmc_string_to_uint (value, TRUE, 1, 13, &chan_int)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid channel; use <1-13>"), value);
		return FALSE;
	}
	g_object_set (setting, property_info->property_name, chan_int, NULL);
	return TRUE;
}

static const char *
_validate_fcn_proxy_pac_script (const char *value, char **out_to_free, GError **error)
{
	char *script = NULL;

	if (!nmc_proxy_check_script (value, &script, error))
		return NULL;
	RETURN_STR_TO_FREE (script);
}

static gconstpointer
_get_fcn_tc_config_qdiscs (ARGS_GET_FCN)
{
	NMSettingTCConfig *s_tc = NM_SETTING_TC_CONFIG (setting);
	GString *printable;
	guint num_qdiscs, i;
	NMTCQdisc *qdisc;
	char *str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_qdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc);
	for (i = 0; i < num_qdiscs; i++) {
		qdisc = nm_setting_tc_config_get_qdisc (s_tc, i);

		if (printable->len > 0)
			g_string_append (printable, ", ");

		str = nm_utils_tc_qdisc_to_str (qdisc, NULL);
		if (str) {
			g_string_append (printable, str);
			g_free (str);
		}
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static gboolean
_set_fcn_tc_config_qdiscs (ARGS_SET_FCN)
{
	gs_free const char **strv = NULL;
	const char *const*iter;
	NMTCQdisc *tc_qdisc;
	GError *local = NULL;

	strv = nm_utils_strsplit_set (value, ",");
	for (iter = strv; strv && *iter; iter++) {
		tc_qdisc = nm_utils_tc_qdisc_from_str (*iter, &local);
		if (!tc_qdisc) {
			g_set_error (error, 1, 0, "%s %s", local->message,
			             _("The valid syntax is: '[root | parent <handle>] [handle <handle>] <qdisc>'"));
			return FALSE;
		}
		nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (setting), tc_qdisc);
		nm_tc_qdisc_unref (tc_qdisc);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_tc_qdisc (NMSettingTCConfig *setting,
                               const char *value,
                               GError **error)
{
	NMTCQdisc *qdisc;
	gboolean ret;

	qdisc = nm_utils_tc_qdisc_from_str (value, error);
	if (!qdisc)
		return FALSE;

	ret = nm_setting_tc_config_remove_qdisc_by_value (setting, qdisc);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain qdisc '%s'"), value);
	nm_tc_qdisc_unref (qdisc);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_tc_config_qdiscs,
                               NM_SETTING_TC_CONFIG,
                               nm_setting_tc_config_get_num_qdiscs,
                               nm_setting_tc_config_remove_qdisc,
                               _validate_and_remove_tc_qdisc)

static gconstpointer
_get_fcn_tc_config_tfilters (ARGS_GET_FCN)
{
	NMSettingTCConfig *s_tc = NM_SETTING_TC_CONFIG (setting);
	GString *printable;
	guint num_tfilters, i;
	NMTCTfilter *tfilter;
	char *str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_tfilters = nm_setting_tc_config_get_num_tfilters (s_tc);
	for (i = 0; i < num_tfilters; i++) {
		tfilter = nm_setting_tc_config_get_tfilter (s_tc, i);

		if (printable->len > 0)
			g_string_append (printable, ", ");

		str = nm_utils_tc_tfilter_to_str (tfilter, NULL);
		if (str) {
			g_string_append (printable, str);
			g_free (str);
		}
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static gboolean
_set_fcn_tc_config_tfilters (ARGS_SET_FCN)
{
	gs_free const char **strv = NULL;
	const char *const*iter;
	NMTCTfilter *tc_tfilter;
	GError *local = NULL;

	strv = nm_utils_strsplit_set (value, ",");
	for (iter = strv; strv && *iter; iter++) {
		tc_tfilter = nm_utils_tc_tfilter_from_str (*iter, &local);
		if (!tc_tfilter) {
			g_set_error (error, 1, 0, "%s %s", local->message,
			             _("The valid syntax is: '[root | parent <handle>] [handle <handle>] <tfilter>'"));
			return FALSE;
		}
		nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (setting), tc_tfilter);
		nm_tc_tfilter_unref (tc_tfilter);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_tc_tfilter (NMSettingTCConfig *setting,
                               const char *value,
                               GError **error)
{
	NMTCTfilter *tfilter;
	gboolean ret;

	tfilter = nm_utils_tc_tfilter_from_str (value, error);
	if (!tfilter)
		return FALSE;

	ret = nm_setting_tc_config_remove_tfilter_by_value (setting, tfilter);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain tfilter '%s'"), value);
	nm_tc_tfilter_unref (tfilter);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_tc_config_tfilters,
                               NM_SETTING_TC_CONFIG,
                               nm_setting_tc_config_get_num_tfilters,
                               nm_setting_tc_config_remove_tfilter,
                               _validate_and_remove_tc_tfilter)

static const char *
_validate_fcn_team_config (const char *value, char **out_to_free, GError **error)
{
	char *json = NULL;

	if (!nmc_team_check_config (value, &json, error))
		return NULL;
	RETURN_STR_TO_FREE (json);
}

static gboolean
_is_valid_team_runner_tx_hash_element (const char *tx_hash_element)
{
	const char *valid_tx_hashes[] = { "eth", "vlan", "ipv4", "ipv6", "ip",
	                                  "l3", "tcp", "udp", "sctp", "l4", NULL };
	if (nmc_string_is_valid (tx_hash_element, valid_tx_hashes, NULL))
		return TRUE;
	return FALSE;
}

static gboolean
_set_fcn_team_runner_tx_hash (ARGS_SET_FCN)
{
	char **strv = NULL;
	guint i = 0;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	strv = _nm_utils_strv_cleanup (g_strsplit_set (value, " \t,", 0),
	                               TRUE, TRUE, TRUE);
	if (!verify_string_list (strv, property_info->property_name,
	                         _is_valid_team_runner_tx_hash_element,
	                         error)) {
		g_strfreev (strv);
		return FALSE;
	}

	while (strv && strv[i])
		nm_setting_team_add_runner_tx_hash (NM_SETTING_TEAM (setting), strv[i++]);
	g_strfreev (strv);

	return TRUE;
}

static gboolean
_validate_and_remove_team_runner_tx_hash (NMSettingTeam *setting,
                                         const char *tx_hash,
                                         GError **error)
{
	if (!nm_setting_team_remove_runner_tx_hash_by_value (setting, tx_hash)) {
		g_set_error (error, 1, 0,
		             _("the property doesn't contain string '%s'"),
		             tx_hash);
		return FALSE;
	}

	return TRUE;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_runner_tx_hash,
                               NM_SETTING_TEAM,
                               nm_setting_team_get_num_runner_tx_hash,
                               nm_setting_team_remove_runner_tx_hash,
                               _validate_and_remove_team_runner_tx_hash)

static gconstpointer
_get_fcn_team_link_watchers (ARGS_GET_FCN)
{
	NMSettingTeam *s_team = NM_SETTING_TEAM (setting);
	GString *printable;
	guint num_watchers, i;
	NMTeamLinkWatcher *watcher;
	char *watcher_str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_watchers = nm_setting_team_get_num_link_watchers (s_team);
	for (i = 0; i < num_watchers; i++) {
		watcher = nm_setting_team_get_link_watcher (s_team, i);
		watcher_str = _dump_team_link_watcher (watcher);
		if (watcher_str) {
			if (printable->len > 0)
				g_string_append (printable, ", ");
			g_string_append (printable, watcher_str);
			g_free (watcher_str);
		}
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static gboolean
_set_fcn_team_link_watchers (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMTeamLinkWatcher *watcher;

	nm_setting_team_clear_link_watchers (NM_SETTING_TEAM (setting));
	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		watcher = _parse_team_link_watcher (*iter, error);
		if (!watcher)
			return FALSE;
		nm_setting_team_add_link_watcher (NM_SETTING_TEAM (setting), watcher);
		nm_team_link_watcher_unref (watcher);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_team_link_watcher (NMSettingTeam *setting,
                                        const char *watcher_str,
                                        GError **error)
{
	NMTeamLinkWatcher *watcher;
	gboolean ret;

	watcher = _parse_team_link_watcher (watcher_str, error);
	if (!watcher)
		return FALSE;

	ret = nm_setting_team_remove_link_watcher_by_value (setting, watcher);
	if (!ret) {
		g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"),
		             watcher_str);
	}
	nm_team_link_watcher_unref (watcher);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_link_watchers,
                               NM_SETTING_TEAM,
                               nm_setting_team_get_num_link_watchers,
                               nm_setting_team_remove_link_watcher,
                               _validate_and_remove_team_link_watcher)

static gconstpointer
_get_fcn_team_port_link_watchers (ARGS_GET_FCN)
{
	NMSettingTeamPort *s_team_port = NM_SETTING_TEAM_PORT (setting);
	GString *printable;
	guint num_watchers, i;
	NMTeamLinkWatcher *watcher;
	char *watcher_str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	printable = g_string_new (NULL);

	num_watchers = nm_setting_team_port_get_num_link_watchers (s_team_port);
	for (i = 0; i < num_watchers; i++) {
		watcher = nm_setting_team_port_get_link_watcher (s_team_port, i);
		watcher_str = _dump_team_link_watcher (watcher);
		if (watcher_str) {
			if (printable->len > 0)
				g_string_append (printable, ", ");
			g_string_append (printable, watcher_str);
			g_free (watcher_str);
		}
	}

	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
}

static gboolean
_set_fcn_team_port_link_watchers (ARGS_SET_FCN)
{
	gs_strfreev char **strv = NULL;
	const char *const*iter;
	NMTeamLinkWatcher *watcher;

	nm_setting_team_port_clear_link_watchers (NM_SETTING_TEAM_PORT (setting));
	strv = nmc_strsplit_set (value, ",", 0);
	for (iter = (const char *const*) strv; *iter; iter++) {
		watcher = _parse_team_link_watcher (*iter, error);
		if (!watcher)
			return FALSE;
		nm_setting_team_port_add_link_watcher (NM_SETTING_TEAM_PORT (setting), watcher);
		nm_team_link_watcher_unref (watcher);
	}
	return TRUE;
}

static gboolean
_validate_and_remove_team_port_link_watcher (NMSettingTeamPort *setting,
                                             const char *watcher_str,
                                             GError **error)
{
	NMTeamLinkWatcher *watcher;
	gboolean ret;

	watcher = _parse_team_link_watcher (watcher_str, error);
	if (!watcher)
		return FALSE;

	ret = nm_setting_team_port_remove_link_watcher_by_value (setting, watcher);
	if (!ret) {
		g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"),
		             watcher_str);
	}
	nm_team_link_watcher_unref (watcher);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_port_link_watchers,
                               NM_SETTING_TEAM_PORT,
                               nm_setting_team_port_get_num_link_watchers,
                               nm_setting_team_port_remove_link_watcher,
                               _validate_and_remove_team_port_link_watcher)

static gconstpointer
_get_fcn_vlan_flags (ARGS_GET_FCN)
{
	NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting);
	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (vlan_flags_to_string (nm_setting_vlan_get_flags (s_vlan), get_type));
}

static gconstpointer
_get_fcn_vlan_ingress_priority_map (ARGS_GET_FCN)
{
	NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting);
	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (vlan_priorities_to_string (s_vlan, NM_VLAN_INGRESS_MAP));
}

static gconstpointer
_get_fcn_vlan_egress_priority_map (ARGS_GET_FCN)
{
	NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting);
	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (vlan_priorities_to_string (s_vlan, NM_VLAN_EGRESS_MAP));
}

static gboolean
_set_vlan_xgress_priority_map (NMSetting *setting,
                               const char *value,
                               NMVlanPriorityMap map_type,
                               GError **error)
{
	char **prio_map, **p;

	prio_map = _parse_vlan_priority_maps (value, map_type, error);
	if (!prio_map)
		return FALSE;

	for (p = prio_map; p && *p; p++)
		nm_setting_vlan_add_priority_str (NM_SETTING_VLAN (setting), map_type, *p);

	g_strfreev (prio_map);
	return TRUE;
}

static gboolean
_set_fcn_vlan_ingress_priority_map (ARGS_SET_FCN)
{
	return _set_vlan_xgress_priority_map (setting, value, NM_VLAN_INGRESS_MAP, error);
}

static gboolean
_set_fcn_vlan_egress_priority_map (ARGS_SET_FCN)
{
	return _set_vlan_xgress_priority_map (setting, value, NM_VLAN_EGRESS_MAP, error);
}

static gboolean
_remove_vlan_xgress_priority_map (const NMMetaEnvironment *environment,
                                  gpointer environment_user_data,
                                  NMSetting *setting,
                                  const NMMetaPropertyInfo *property_info,
                                  const char *value,
                                  guint32 idx,
                                  NMVlanPriorityMap map_type,
                                  GError **error)
{
	guint32 num;

	/* If value != NULL, remove by value */
	if (value) {
		gboolean ret;
		char **prio_map;
		gs_free char *v = g_strdup (value);

		prio_map = _parse_vlan_priority_maps (v, map_type, error);
		if (!prio_map)
			return FALSE;
		if (prio_map[1]) {
			_env_warn_fcn (environment, environment_user_data,
			               NM_META_ENV_WARN_LEVEL_WARN,
			               N_("only one mapping at a time is supported; taking the first one (%s)"),
			               prio_map[0]);
		}
		ret = nm_setting_vlan_remove_priority_str_by_value (NM_SETTING_VLAN (setting),
		                                                    map_type,
		                                                    prio_map[0]);

		if (!ret)
			g_set_error (error, 1, 0, _("the property doesn't contain mapping '%s'"), prio_map[0]);
		g_strfreev (prio_map);
		return ret;
	}

	/* Else remove by index */
	num = nm_setting_vlan_get_num_priorities (NM_SETTING_VLAN (setting), map_type);
	if (num == 0) {
		g_set_error_literal (error, 1, 0, _("no priority to remove"));
		return FALSE;
	}
	if (idx >= num) {
		g_set_error (error, 1, 0, _("index '%d' is not in the range of <0-%d>"),
		             idx, num - 1);
		return FALSE;
	}

	nm_setting_vlan_remove_priority (NM_SETTING_VLAN (setting), map_type, idx);
	return TRUE;
}

static gboolean
_remove_fcn_vlan_ingress_priority_map (ARGS_REMOVE_FCN)
{
	return _remove_vlan_xgress_priority_map (environment,
	                                         environment_user_data,
	                                         setting,
	                                         property_info,
	                                         value,
	                                         idx,
	                                         NM_VLAN_INGRESS_MAP,
	                                         error);
}

static gboolean
_remove_fcn_vlan_egress_priority_map (ARGS_REMOVE_FCN)
{
	return _remove_vlan_xgress_priority_map (environment,
	                                         environment_user_data,
	                                         setting,
	                                         property_info,
	                                         value,
	                                         idx,
	                                         NM_VLAN_EGRESS_MAP,
	                                         error);
}

static gconstpointer
_get_fcn_vpn_data (ARGS_GET_FCN)
{
	NMSettingVpn *s_vpn = NM_SETTING_VPN (setting);
	GString *data_item_str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	data_item_str = g_string_new (NULL);
	nm_setting_vpn_foreach_data_item (s_vpn, &vpn_data_item, data_item_str);

	RETURN_STR_TO_FREE (g_string_free (data_item_str, FALSE));
}

static gconstpointer
_get_fcn_vpn_secrets (ARGS_GET_FCN)
{
	NMSettingVpn *s_vpn = NM_SETTING_VPN (setting);
	GString *secret_str;

	RETURN_UNSUPPORTED_GET_TYPE ();

	secret_str = g_string_new (NULL);
	nm_setting_vpn_foreach_secret (s_vpn, &vpn_data_item, secret_str);

	RETURN_STR_TO_FREE (g_string_free (secret_str, FALSE));
}

static const char *
_validate_vpn_hash_value (const char *option, const char *value, GError **error)
{
	/* nm_setting_vpn_add_data_item() and nm_setting_vpn_add_secret() does not
	 * allow empty strings */
	if (!value || !*value) {
		g_set_error (error, 1, 0, _("'%s' cannot be empty"), option);
		return NULL;
	}
	return value;
}

DEFINE_SETTER_OPTIONS (_set_fcn_vpn_data,
                       NM_SETTING_VPN,
                       NMSettingVpn,
                       nm_setting_vpn_add_data_item,
                       NULL,
                       _validate_vpn_hash_value)
DEFINE_REMOVER_OPTION (_remove_fcn_vpn_data,
                       NM_SETTING_VPN,
                       nm_setting_vpn_remove_data_item)

DEFINE_SETTER_OPTIONS (_set_fcn_vpn_secrets,
                       NM_SETTING_VPN,
                       NMSettingVpn,
                       nm_setting_vpn_add_secret,
                       NULL,
                       _validate_vpn_hash_value)
DEFINE_REMOVER_OPTION (_remove_fcn_vpn_secrets,
                       NM_SETTING_VPN,
                       nm_setting_vpn_remove_secret)

DEFINE_SETTER_MAC_BLACKLIST (_set_fcn_wired_mac_address_blacklist,
                             NM_SETTING_WIRED,
                             nm_setting_wired_add_mac_blacklist_item)

static gboolean
_validate_and_remove_wired_mac_blacklist_item (NMSettingWired *setting,
                                              const char *mac,
                                              GError **error)
{
	gboolean ret;
	guint8 buf[32];

	if (!nm_utils_hwaddr_aton (mac, buf, ETH_ALEN)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac);
                return FALSE;
	}

	ret = nm_setting_wired_remove_mac_blacklist_item_by_value (setting, mac);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_wired_mac_address_blacklist,
                               NM_SETTING_WIRED,
                               nm_setting_wired_get_num_mac_blacklist_items,
                               nm_setting_wired_remove_mac_blacklist_item,
                               _validate_and_remove_wired_mac_blacklist_item)

static gboolean
_set_fcn_wired_s390_subchannels (ARGS_SET_FCN)
{
	char **strv = NULL;
	int len;

	strv = nmc_strsplit_set (value, " ,\t", 0);
	len = g_strv_length (strv);
	if (len != 2 && len != 3) {
		g_set_error (error, 1, 0, _("'%s' is not valid; 2 or 3 strings should be provided"),
		             value);
		g_strfreev (strv);
		return FALSE;
	}

	g_object_set (setting, property_info->property_name, strv, NULL);
	g_strfreev (strv);
	return TRUE;
}

static const char *
_validate_s390_option_value (const char *option, const char *value, GError **error)
{
	/*  nm_setting_wired_add_s390_option() requires value len in <1,199> interval */
	if (!value || !*value || strlen (value) >= 200) {
		g_set_error (error, 1, 0, _("'%s' string value should consist of 1 - 199 characters"), option);
		return NULL;
	}
	return value;
}
DEFINE_SETTER_OPTIONS (_set_fcn_wired_s390_options,
                       NM_SETTING_WIRED,
                       NMSettingWired,
                       nm_setting_wired_add_s390_option,
                       nm_setting_wired_get_valid_s390_options,
                       _validate_s390_option_value)
DEFINE_REMOVER_OPTION (_remove_fcn_wired_s390_options,
                       NM_SETTING_WIRED,
                       nm_setting_wired_remove_s390_option)

static const char *const*
_values_fcn__wired_s390_options (ARGS_VALUES_FCN)
{
	return nm_setting_wired_get_valid_s390_options (NULL);
}

static const char *
_describe_fcn_wired_s390_options (ARGS_DESCRIBE_FCN)
{
	gs_free char *options_str = NULL;
	const char **valid_options;
	char *s;

	valid_options = nm_setting_wired_get_valid_s390_options (NULL);

	options_str = g_strjoinv (", ", (char **) valid_options);

	s = g_strdup_printf (_("Enter a list of S/390 options formatted as:\n"
	                       "  option = <value>, option = <value>,...\n"
	                       "Valid options are: %s\n"),
	                       options_str);
	return (*out_to_free = s);
}


static gconstpointer
_get_fcn_wireless_ssid (ARGS_GET_FCN)
{
	NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting);
	GBytes *ssid;
	char *ssid_str = NULL;

	RETURN_UNSUPPORTED_GET_TYPE ();

	ssid = nm_setting_wireless_get_ssid (s_wireless);
	if (ssid) {
		ssid_str = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
		                                  g_bytes_get_size (ssid));
	}

	RETURN_STR_TO_FREE (ssid_str);
}

static gboolean
_set_fcn_wireless_channel (ARGS_SET_FCN)
{
	unsigned long chan_int;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (!nmc_string_to_uint (value, FALSE, 0, 0, &chan_int)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid channel"), value);
		return FALSE;
	}

	if (   !nm_utils_wifi_is_channel_valid (chan_int, "a")
	    && !nm_utils_wifi_is_channel_valid (chan_int, "bg")) {
		g_set_error (error, 1, 0, _("'%ld' is not a valid channel"), chan_int);
		return FALSE;
	}

	g_object_set (setting, property_info->property_name, chan_int, NULL);
	return TRUE;
}

DEFINE_SETTER_MAC_BLACKLIST (_set_fcn_wireless_mac_address_blacklist,
                             NM_SETTING_WIRELESS,
                             nm_setting_wireless_add_mac_blacklist_item)

static gboolean
_validate_and_remove_wifi_mac_blacklist_item (NMSettingWireless *setting,
                                              const char *mac,
                                              GError **error)
{
	gboolean ret;
	guint8 buf[32];

	if (!nm_utils_hwaddr_aton (mac, buf, ETH_ALEN)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac);
                return FALSE;
	}

	ret = nm_setting_wireless_remove_mac_blacklist_item_by_value (setting, mac);
	if (!ret)
		g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_wireless_mac_address_blacklist,
                               NM_SETTING_WIRELESS,
                               nm_setting_wireless_get_num_mac_blacklist_items,
                               nm_setting_wireless_remove_mac_blacklist_item,
                               _validate_and_remove_wifi_mac_blacklist_item)

static gconstpointer
_get_fcn_wireless_security_wep_key0 (ARGS_GET_FCN)
{
	NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting);

	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0)));
}

static gconstpointer
_get_fcn_wireless_security_wep_key1 (ARGS_GET_FCN)
{
	NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting);

	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)));
}

static gconstpointer
_get_fcn_wireless_security_wep_key2 (ARGS_GET_FCN)
{
	NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting);

	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)));
}

static gconstpointer
_get_fcn_wireless_security_wep_key3 (ARGS_GET_FCN)
{
	NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting);

	RETURN_UNSUPPORTED_GET_TYPE ();
	RETURN_STR_TO_FREE (g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)));
}

static const char *wifi_sec_valid_protos[] = { "wpa", "rsn", NULL };

DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_proto,
                              NM_SETTING_WIRELESS_SECURITY,
                              nm_setting_wireless_security_add_proto)

static gboolean
_set_fcn_wireless_security_proto (ARGS_SET_FCN)
{
	return check_and_add_wifi_sec_proto (setting, property_info->property_name, value, wifi_sec_valid_protos, error);
}

static gboolean
_validate_and_remove_wifi_sec_proto (NMSettingWirelessSecurity *setting,
                                     const char *proto,
                                     GError **error)
{
	gboolean ret;
	const char *valid;

	valid = nmc_string_is_valid (proto, wifi_sec_valid_protos, error);
	if (!valid)
		return FALSE;

	ret = nm_setting_wireless_security_remove_proto_by_value (setting, proto);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain protocol '%s'"), proto);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_wireless_security_proto,
                               NM_SETTING_WIRELESS_SECURITY,
                               nm_setting_wireless_security_get_num_protos,
                               nm_setting_wireless_security_remove_proto,
                               _validate_and_remove_wifi_sec_proto)

static const char *wifi_sec_valid_pairwises[] = { "tkip", "ccmp", NULL };

DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_pairwise,
                              NM_SETTING_WIRELESS_SECURITY,
                              nm_setting_wireless_security_add_pairwise)

static gboolean
_set_fcn_wireless_security_pairwise (ARGS_SET_FCN)
{
	return check_and_add_wifi_sec_pairwise (setting, property_info->property_name, value, wifi_sec_valid_pairwises, error);
}

static gboolean
_validate_and_remove_wifi_sec_pairwise (NMSettingWirelessSecurity *setting,
                                        const char *pairwise,
                                        GError **error)
{
	gboolean ret;
	const char *valid;

	valid = nmc_string_is_valid (pairwise, wifi_sec_valid_pairwises, error);
	if (!valid)
		return FALSE;

	ret = nm_setting_wireless_security_remove_pairwise_by_value (setting, pairwise);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain protocol '%s'"), pairwise);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_wireless_security_pairwise,
                               NM_SETTING_WIRELESS_SECURITY,
                               nm_setting_wireless_security_get_num_pairwise,
                               nm_setting_wireless_security_remove_pairwise,
                               _validate_and_remove_wifi_sec_pairwise)

static const char *wifi_sec_valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL };

DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_group,
                              NM_SETTING_WIRELESS_SECURITY,
                              nm_setting_wireless_security_add_group)

static gboolean
_set_fcn_wireless_security_group (ARGS_SET_FCN)
{
	return check_and_add_wifi_sec_group (setting, property_info->property_name, value, wifi_sec_valid_groups, error);
}

static gboolean
_validate_and_remove_wifi_sec_group (NMSettingWirelessSecurity *setting,
                                     const char *group,
                                     GError **error)
{
	gboolean ret;
	const char *valid;

	valid = nmc_string_is_valid (group, wifi_sec_valid_groups, error);
	if (!valid)
		return FALSE;

	ret = nm_setting_wireless_security_remove_group_by_value (setting, group);
	if (!ret)
		g_set_error (error, 1, 0,
		             _("the property doesn't contain protocol '%s'"), group);
	return ret;
}
DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_wireless_security_group,
                               NM_SETTING_WIRELESS_SECURITY,
                               nm_setting_wireless_security_get_num_groups,
                               nm_setting_wireless_security_remove_group,
                               _validate_and_remove_wifi_sec_group)

static gboolean
_set_fcn_wireless_wep_key (ARGS_SET_FCN)
{
	NMWepKeyType guessed_type = NM_WEP_KEY_TYPE_UNKNOWN;
	NMWepKeyType type;
	guint32 prev_idx, idx;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	/* Get currently set type */
	type = nm_setting_wireless_security_get_wep_key_type (NM_SETTING_WIRELESS_SECURITY (setting));

	/* Guess key type */
	if (nm_utils_wep_key_valid (value, NM_WEP_KEY_TYPE_KEY))
		guessed_type = NM_WEP_KEY_TYPE_KEY;
	else if (nm_utils_wep_key_valid (value, NM_WEP_KEY_TYPE_PASSPHRASE))
		guessed_type = NM_WEP_KEY_TYPE_PASSPHRASE;

	if (guessed_type == NM_WEP_KEY_TYPE_UNKNOWN) {
		g_set_error (error, 1, 0, _("'%s' is not valid"), value);
		return FALSE;
	}

	if (type != NM_WEP_KEY_TYPE_UNKNOWN && type != guessed_type) {
		if (nm_utils_wep_key_valid (value, type))
			guessed_type = type;
		else {
			g_set_error (error, 1, 0,
			             _("'%s' not compatible with %s '%s', please change the key or set the right %s first."),
			             value, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_key_type_to_string (type),
			             NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE);
			return FALSE;
		}
	}
	prev_idx = nm_setting_wireless_security_get_wep_tx_keyidx (NM_SETTING_WIRELESS_SECURITY (setting));
	idx = property_info->property_name[strlen (property_info->property_name) - 1] - '0';
	_env_warn_fcn (environment, environment_user_data,
	               NM_META_ENV_WARN_LEVEL_INFO,
	               N_("WEP key is guessed to be of '%s'"),
	               wep_key_type_to_string (guessed_type));
	if (idx != prev_idx) {
		_env_warn_fcn (environment, environment_user_data,
		               NM_META_ENV_WARN_LEVEL_INFO,
		               N_("WEP key index set to '%d'"),
		               idx);
	}

	g_object_set (setting, property_info->property_name, value, NULL);
	g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, guessed_type, NULL);
	if (idx != prev_idx)
		g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, idx, NULL);
	return TRUE;
}

static void
_gobject_enum_pre_set_notify_fcn_wireless_security_wep_key_type (const NMMetaPropertyInfo *property_info,
                                                                 const NMMetaEnvironment *environment,
                                                                 gpointer environment_user_data,
                                                                 NMSetting *setting,
                                                                 int value)
{
	guint i;
	const char *key;
	const char *keynames[] = {
		NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
		NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
		NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
		NM_SETTING_WIRELESS_SECURITY_WEP_KEY3,
	};

	/* Check type compatibility with set keys */
	if (!NM_IN_SET (value,
	                NM_WEP_KEY_TYPE_UNKNOWN,
	                NM_WEP_KEY_TYPE_KEY,
	                NM_WEP_KEY_TYPE_PASSPHRASE))
		return;

	for (i = 0; i < 4; i++) {
		key = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), i);
		if (key && !nm_utils_wep_key_valid (key, value)) {
			_env_warn_fcn (environment, environment_user_data,
			               NM_META_ENV_WARN_LEVEL_WARN,
			               N_("'%s' is not compatible with '%s' type, please change or delete the key."),
			               keynames[i], wep_key_type_to_string (value));
		}
	}
}

static const char *
_validate_fcn_wireless_security_psk (const char *value, char **out_to_free, GError **error)
{
	if (!nm_utils_wpa_psk_valid (value)) {
		g_set_error (error, 1, 0, _("'%s' is not a valid PSK"), value);
		return NULL;
	}
	return value;
}

/*****************************************************************************/

static const NMMetaPropertyInfo property_info_BOND_OPTIONS;

#define NESTED_PROPERTY_INFO_BOND(...) \
	.parent_info =                  &property_info_BOND_OPTIONS, \
	.base = { \
		.meta_type =                &nm_meta_type_nested_property_info, \
		.setting_info =             &nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_BOND], \
		__VA_ARGS__ \
	}

static const NMMetaNestedPropertyInfo meta_nested_property_infos_bond[] = {
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "mode",
			.prompt =                   NM_META_TEXT_PROMPT_BOND_MODE,
			.def_hint =                 "[balance-rr]",
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "primary",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding primary interface [none]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			/* this is a virtual property, only needed during "ask" mode. */
			.prompt =                   N_("Bonding monitoring mode"),
			.def_hint =                 NM_META_TEXT_PROMPT_BOND_MON_MODE_CHOICES,
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "miimon",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding miimon [100]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "downdelay",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding downdelay [0]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "updelay",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding updelay [0]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "arp-interval",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding arp-interval [0]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "arp-ip-target",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("Bonding arp-ip-target [none]"),
		)
	},
	{
		NESTED_PROPERTY_INFO_BOND (
			.property_name =            NM_SETTING_BOND_OPTIONS,
			.property_alias =           "lacp-rate",
			.inf_flags =                NM_META_PROPERTY_INF_FLAG_DONT_ASK,
			.prompt =                   N_("LACP rate ('slow' or 'fast') [slow]"),
		)
	},
};

const NMMetaPropertyTypDataNested nm_meta_property_typ_data_bond = {
	.nested =               meta_nested_property_infos_bond,
	.nested_len =           G_N_ELEMENTS (meta_nested_property_infos_bond),
};

/*****************************************************************************/

#define DEFINE_PROPERTY_TYPE(...) \
	(&((NMMetaPropertyType) { __VA_ARGS__ } ))

#define DEFINE_PROPERTY_TYP_DATA(...) \
	(&((NMMetaPropertyTypData) { __VA_ARGS__ } ))

#define PROPERTY_TYP_DATA_SUBTYPE(stype, ...) \
	.subtype = { \
		.stype = { __VA_ARGS__ }, \
	}

#define DEFINE_PROPERTY_TYP_DATA_SUBTYPE(stype, ...) \
	DEFINE_PROPERTY_TYP_DATA ( \
		PROPERTY_TYP_DATA_SUBTYPE (stype, __VA_ARGS__), \
	)

static const NMMetaPropertyType _pt_gobject_readonly = {
	.get_fcn =                      _get_fcn_gobject,
};

static const NMMetaPropertyType _pt_gobject_string = {
	.get_fcn =                      _get_fcn_gobject,
	.set_fcn =                      _set_fcn_gobject_string,
};

static const NMMetaPropertyType _pt_gobject_bool = {
	.get_fcn =                      _get_fcn_gobject,
	.set_fcn =                      _set_fcn_gobject_bool,
	.complete_fcn =                 _complete_fcn_gobject_bool,
};

static const NMMetaPropertyType _pt_gobject_int = {
	.get_fcn =                      _get_fcn_gobject_int,
	.set_fcn =                      _set_fcn_gobject_int,
};

static const NMMetaPropertyType _pt_gobject_mtu = {
	.get_fcn =                      _get_fcn_gobject_mtu,
	.set_fcn =                      _set_fcn_gobject_mtu,
};

static const NMMetaPropertyType _pt_gobject_mac = {
	.get_fcn =                      _get_fcn_gobject,
	.set_fcn =                      _set_fcn_gobject_mac,
};

static const NMMetaPropertyType _pt_gobject_secret_flags = {
	.get_fcn =                      _get_fcn_gobject_secret_flags,
	.set_fcn =                      _set_fcn_gobject_secret_flags,
};

static const NMMetaPropertyType _pt_gobject_enum = {
	.get_fcn =                      _get_fcn_gobject_enum,
	.set_fcn =                      _set_fcn_gobject_enum,
	.values_fcn =                   _values_fcn_gobject_enum,
};

static const NMMetaPropertyType _pt_gobject_devices = {
	.get_fcn =                      _get_fcn_gobject,
	.set_fcn =                      _set_fcn_gobject_string,
	.complete_fcn =                 _complete_fcn_gobject_devices,
};

/*****************************************************************************/

#include "settings-docs.c"

/*****************************************************************************/

#define PROPERTY_INFO_INIT(name, doc, ...) \
	{ \
		.meta_type =                    &nm_meta_type_property_info, \
		.setting_info =                 &nm_meta_setting_infos_editor[_CURRENT_NM_META_SETTING_TYPE], \
		.property_name =                name, \
		.describe_doc =                 doc, \
		__VA_ARGS__ \
	}

#define PROPERTY_INFO(name, doc, ...) \
	(&((const NMMetaPropertyInfo) PROPERTY_INFO_INIT (name, doc, __VA_ARGS__)))

#define PROPERTY_INFO_WITH_DESC(name, ...) \
	PROPERTY_INFO (name, DESCRIBE_DOC_##name, ##__VA_ARGS__)

#define VALUES_STATIC(...)  (((const char *[]) { __VA_ARGS__, NULL }))

#define ENUM_VALUE_INFOS(...)  (((const NMUtilsEnumValueInfo []) { __VA_ARGS__, { 0 } }))
#define INT_VALUE_INFOS(...)  (((const NMMetaUtilsIntValueInfo []) { __VA_ARGS__, { 0 } }))

#define GET_FCN_WITH_DEFAULT(type, func) \
	/* macro that returns @func as const (gboolean(*)(NMSetting*)) type, but checks
	 * that the actual type is (gboolean(*)(type *)). */ \
	((gboolean (*) (NMSetting *)) ((sizeof (func == ((gboolean (*) (type *)) func))) ? func : func) )

#define MTU_GET_FCN(type, func) \
	/* macro that returns @func as const (guint32(*)(NMSetting*)) type, but checks
	 * that the actual type is (guint32(*)(type *)). */ \
	((guint32 (*) (NMSetting *)) ((sizeof (func == ((guint32 (*) (type *)) func))) ? func : func) )

#define TEAM_DESCRIBE_MESSAGE \
	N_("nmcli can accepts both direct JSON configuration data and a file name containing " \
	   "the configuration. In the latter case the file is read and the contents is put " \
	   "into this property.\n\n" \
	   "Examples: set team.config " \
	   "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" \
	   "          set team.config /etc/my-team.conf\n")

#define TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE \
	N_("Enter a list of link watchers formatted as dictionaries where the keys " \
	   "are teamd properties. Dictionary pairs are in the form: key=value and pairs " \
	   "are separated by ' '. Dictionaries are separated with ','.\n" \
	   "The keys allowed/required in the dictionary change on the basis of the link " \
	   "watcher type, while the only property common to all the link watchers is " \
	   " 'name'*, which defines the link watcher to be specified.\n\n" \
	   "Properties available for the 'ethtool' link watcher:\n" \
	   "  'delay-up', 'delay-down'\n\n" \
	   "Properties available for the 'nsna_ping' link watcher:\n" \
	   "  'init-wait', 'interval', 'missed-max', 'target-host'*\n\n" \
	   "Properties available for the 'arp_ping' include all the ones for 'nsna_ping' and:\n" \
	   "  'source-host'*, 'validate-active', 'validate-inactive', 'send-always'.\n\n" \
	   "Properties flagged with a '*' are mandatory.\n\n" \
	   "Example:\n" \
	   "   name=arp_ping source-host=172.16.1.1 target-host=172.16.1.254, name=ethtool delay-up=3\n")


#define DEFINE_DCB_PROPRITY_PROPERTY_TYPE \
		.property_type =                &_pt_gobject_int, \
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
			.value_infos =              INT_VALUE_INFOS ( \
				{ \
					.value = -1, \
					.nick = "unset", \
				} \
			), \
		),

#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_802_1X
static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_EAP,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_802_1x_eap,
			.remove_fcn =               _remove_fcn_802_1x_eap,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_IDENTITY,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PAC_FILE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CA_CERT,
		.describe_message =
		    N_("Enter file path to CA certificate (optionally prefixed with file://).\n"
		       "  [file://]<file path>\n"
		       "Note that nmcli does not support specifying certificates as raw blob data.\n"
		       "Example: /home/cimrman/cacert.crt\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_ca_cert,
			.set_fcn =                  _set_fcn_802_1x_ca_cert,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CA_CERT_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CA_PATH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_SUBJECT_MATCH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_ALTSUBJECT_MATCHES,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_802_1x_altsubject_matches,
			.remove_fcn =               _remove_fcn_802_1x_altsubject_matches,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CLIENT_CERT,
		.describe_message =
		    N_("Enter file path to client certificate (optionally prefixed with file://).\n"
		       "  [file://]<file path>\n"
		       "Note that nmcli does not support specifying certificates as raw blob data.\n"
		       "Example: /home/cimrman/jara.crt\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_client_cert,
			.set_fcn =                  _set_fcn_802_1x_client_cert,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_PEAPVER,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("0", "1"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_PEAPLABEL,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("0", "1"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("0", "1", "2", "3"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_802_1x_auth_flags_get_type,
			),
			.typ_flags =                NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_AUTH,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_AUTHEAP,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("md5", "mschapv2", "otp", "gtc", "tls"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_CERT,
		.describe_message =
		    N_("Enter file path to CA certificate for inner authentication (optionally prefixed\n"
		       "with file://).\n"
		       "  [file://]<file path>\n"
		       "Note that nmcli does not support specifying certificates as raw blob data.\n"
		       "Example: /home/cimrman/ca-zweite-phase.crt\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_phase2_ca_cert,
			.set_fcn =                  _set_fcn_802_1x_phase2_ca_cert,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_PATH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_802_1x_phase2_altsubject_matches,
			.remove_fcn =               _remove_fcn_802_1x_phase2_altsubject_matches,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
		.describe_message =
		    N_("Enter file path to client certificate for inner authentication (optionally prefixed\n"
		       "with file://).\n"
		       "  [file://]<file path>\n"
		       "Note that nmcli does not support specifying certificates as raw blob data.\n"
		       "Example: /home/cimrman/jara-zweite-phase.crt\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_phase2_client_cert,
			.set_fcn =                  _set_fcn_802_1x_phase2_client_cert,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PASSWORD_RAW,
		.is_secret =                    TRUE,
		.describe_message =
		    N_("Enter bytes as a list of hexadecimal values.\n"
		       "Two formats are accepted:\n"
		       "(a) a string of hexadecimal digits, where each two digits represent one byte\n"
		       "(b) space-separated list of bytes written as hexadecimal digits "
		       "(with optional 0x/0X prefix, and optional leading 0).\n\n"
		       "Examples: ab0455a6ea3a74C2\n"
		       "          ab 4 55 0xa6 ea 3a 74 C2\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_password_raw,
			.set_fcn =                  _set_fcn_802_1x_password_raw,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PRIVATE_KEY,
		.describe_message =
		    N_("Enter path to a private key and the key password (if not set yet):\n"
		       "  [file://]<file path> [<password>]\n"
		       "Note that nmcli does not support specifying private key as raw blob data.\n"
		       "Example: /home/cimrman/jara-priv-key Dardanely\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_private_key,
			.set_fcn =                  _set_fcn_802_1x_private_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
		.describe_message =
		    N_("Enter path to a private key and the key password (if not set yet):\n"
		       "  [file://]<file path> [<password>]\n"
		       "Note that nmcli does not support specifying private key as raw blob data.\n"
		       "Example: /home/cimrman/jara-priv-key Dardanely\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_802_1x_phase2_private_key,
			.set_fcn =                  _set_fcn_802_1x_phase2_private_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PIN,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PIN_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_SYSTEM_CA_CERTS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_AUTH_TIMEOUT,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_ADSL
static const NMMetaPropertyInfo *const property_infos_ADSL[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_USERNAME,
		.is_cli_option =                TRUE,
		.property_alias =               "username",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("Username"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_PASSWORD,
		.is_cli_option =                TRUE,
		.property_alias =               "password",
		.prompt =                       N_("Password [none]"),
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_PROTOCOL,
		.is_cli_option =                TRUE,
		.property_alias =               "protocol",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_ADSL_PROTO,
		.def_hint =                     NM_META_TEXT_PROMPT_ADSL_PROTO_CHOICES,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_ADSL_PROTOCOL_PPPOA,
			                                           NM_SETTING_ADSL_PROTOCOL_PPPOE,
			                                           NM_SETTING_ADSL_PROTOCOL_IPOATM),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_ENCAPSULATION,
		.is_cli_option =                TRUE,
		.property_alias =               "encapsulation",
		.prompt =                       NM_META_TEXT_PROMPT_ADSL_ENCAP,
		.def_hint =                     NM_META_TEXT_PROMPT_ADSL_ENCAP_CHOICES,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
			                                           NM_SETTING_ADSL_ENCAPSULATION_LLC),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_VPI,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_VCI,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_BLUETOOTH
static const NMMetaPropertyInfo *const property_infos_BLUETOOTH[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BLUETOOTH_BDADDR,
		.is_cli_option =                TRUE,
		.property_alias =               "addr",
		.prompt =                       N_("Bluetooth device address"),
		.property_type =                &_pt_gobject_mac,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BLUETOOTH_TYPE,
		.is_cli_option =                TRUE,
		.property_alias =               "bt-type",
		.prompt =                       NM_META_TEXT_PROMPT_BT_TYPE,
		.def_hint =                     NM_META_TEXT_PROMPT_BT_TYPE_CHOICES,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_BLUETOOTH_TYPE_DUN,
			                                           NM_SETTING_BLUETOOTH_TYPE_PANU,
			                                           NM_SETTING_BLUETOOTH_TYPE_NAP),
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_BOND
static const NMMetaPropertyInfo property_info_BOND_OPTIONS =
	PROPERTY_INFO_INIT (NM_SETTING_BOND_OPTIONS, DESCRIBE_DOC_NM_SETTING_BOND_OPTIONS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.describe_fcn =             _describe_fcn_bond_options,
			.get_fcn =                  _get_fcn_bond_options,
			.set_fcn =                  _set_fcn_bond_options,
			.remove_fcn =               _remove_fcn_bond_options,
			.values_fcn =               _values_fcn_bond_options,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.nested =                   &nm_meta_property_typ_data_bond,
		),
	);

static const NMMetaPropertyInfo *const property_infos_BOND[] = {
	&property_info_BOND_OPTIONS,
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_BRIDGE
static const NMMetaPropertyInfo *const property_infos_BRIDGE[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "mac",
		.prompt =                       N_("MAC [none]"),
		.property_type =                &_pt_gobject_mac,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_STP,
		.is_cli_option =                TRUE,
		.property_alias =               "stp",
		.prompt =                       N_("Enable STP [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PRIORITY,
		.is_cli_option =                TRUE,
		.property_alias =               "priority",
		.prompt =                       N_("STP priority [32768]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_FORWARD_DELAY,
		.is_cli_option =                TRUE,
		.property_alias =               "forward-delay",
		.prompt =                       N_("Forward delay [15]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_HELLO_TIME,
		.is_cli_option =                TRUE,
		.property_alias =               "hello-time",
		.prompt =                       N_("Hello time [2]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_MAX_AGE,
		.is_cli_option =                TRUE,
		.property_alias =               "max-age",
		.prompt =                       N_("Max age [20]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_AGEING_TIME,
		.is_cli_option =                TRUE,
		.property_alias =               "ageing-time",
		.prompt =                       N_("MAC address ageing time [300]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,
		.is_cli_option =                TRUE,
		.property_alias =               "group-forward-mask",
		.prompt =                       N_("Group forward mask [0]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_MULTICAST_SNOOPING,
		.is_cli_option =                TRUE,
		.property_alias =               "multicast-snooping",
		.prompt =                       N_("Enable IGMP snooping [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_BRIDGE_PORT
static const NMMetaPropertyInfo *const property_infos_BRIDGE_PORT[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PORT_PRIORITY,
		.is_cli_option =                TRUE,
		.property_alias =               "priority",
		.prompt =                       N_("Bridge port priority [32]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PORT_PATH_COST,
		.is_cli_option =                TRUE,
		.property_alias =               "path-cost",
		.prompt =                       N_("Bridge port STP path cost [100]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "hairpin",
		.prompt =                       N_("Hairpin [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_CDMA
static const NMMetaPropertyInfo *const property_infos_CDMA[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CDMA_NUMBER,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CDMA_USERNAME,
		.is_cli_option =                TRUE,
		.property_alias =               "user",
		.prompt =                       N_("Username [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CDMA_PASSWORD,
		.is_cli_option =                TRUE,
		.property_alias =               "password",
		.prompt =                       N_("Password [none]"),
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CDMA_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CDMA_MTU,
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingCdma, nm_setting_cdma_get_mtu),
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_CONNECTION
static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_ID,
		.is_cli_option =                TRUE,
		.property_alias =               "con-name",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_DONT_ASK,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_UUID,
		.property_type =                DEFINE_PROPERTY_TYPE ( .get_fcn = _get_fcn_gobject ),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_STABLE_ID,
		.property_type =                &_pt_gobject_string,
	),
[_NM_META_PROPERTY_TYPE_CONNECTION_TYPE] =
		PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_TYPE,
			.is_cli_option =                TRUE,
			.property_alias =               "type",
			.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
			.prompt =                       NM_META_TEXT_PROMPT_CON_TYPE,
			.property_type = DEFINE_PROPERTY_TYPE (
				.get_fcn =                  _get_fcn_gobject,
				.set_fcn =                  _set_fcn_connection_type,
				.complete_fcn =             _complete_fcn_connection_type,
			),
		),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_INTERFACE_NAME,
		.is_cli_option =                TRUE,
		.property_alias =               "ifname",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_IFNAME,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_gobject_ifname,
			.complete_fcn =             _complete_fcn_gobject_devices,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT,
		.is_cli_option =                TRUE,
		.property_alias =               "autoconnect",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_DONT_ASK,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =              INT_VALUE_INFOS (
				{
					.value = -1,
					.nick = "default",
				},
				{
					.value = 0,
					.nick = "forever",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTH_RETRIES,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_TIMESTAMP,
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_READ_ONLY,
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_PERMISSIONS,
		.describe_message =
		     N_("Enter a list of user permissions. This is a list of user names formatted as:\n"
		        "  [user:]<user name 1>, [user:]<user name 2>,...\n"
		        "The items can be separated by commas or spaces.\n\n"
		        "Example: alice bob charlie\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_connection_permissions,
			.set_fcn =                  _set_fcn_connection_permissions,
			.remove_fcn =               _remove_fcn_connection_permissions,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_ZONE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_MASTER,
		.is_cli_option =                TRUE,
		.property_alias =               "master",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_DONT_ASK,
		.prompt =                       NM_META_TEXT_PROMPT_MASTER,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_connection_master,
			.complete_fcn =             _complete_fcn_connection_master,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_SLAVE_TYPE,
		.is_cli_option =                TRUE,
		.property_alias =               "slave-type",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_DONT_ASK,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_BOND_SETTING_NAME,
			                                           NM_SETTING_BRIDGE_SETTING_NAME,
			                                           NM_SETTING_OVS_BRIDGE_SETTING_NAME,
			                                           NM_SETTING_OVS_PORT_SETTING_NAME,
			                                           NM_SETTING_TEAM_SETTING_NAME),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
		.property_type =                &_pt_gobject_enum,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_SECONDARIES,
		.describe_message =
		    N_("Enter secondary connections that should be activated when this connection is\n"
		       "activated. Connections can be specified either by UUID or ID (name). nmcli\n"
		       "transparently translates names to UUIDs. Note that NetworkManager only supports\n"
		       "VPNs as secondary connections at the moment.\n"
		       "The items can be separated by commas or spaces.\n\n"
		       "Example: private-openvpn, fe6ba5d8-c2fc-4aae-b2e3-97efddd8d9a7\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_connection_secondaries,
			.remove_fcn =               _remove_fcn_connection_secondaries,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_METERED,
		.describe_message =
		    N_("Enter a value which indicates whether the connection is subject to a data\n"
		       "quota, usage costs or other limitations. Accepted options are:\n"
		       "'true','yes','on' to set the connection as metered\n"
		       "'false','no','off' to set the connection as not metered\n"
		       "'unknown' to let NetworkManager choose a value using some heuristics\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_connection_metered,
			.set_fcn =                  _set_fcn_connection_metered,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("yes", "no", "unknown"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_LLDP,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_connection_lldp_get_type,
				.value_infos =          ENUM_VALUE_INFOS (
					{
						.value = NM_SETTING_CONNECTION_LLDP_ENABLE_RX,
						.nick = "enable",
					}
				),
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_DCB
static const NMMetaPropertyInfo *const property_infos_DCB[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FCOE_FLAGS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_app_fcoe_flags,
			.set_fcn =                  _set_fcn_dcb_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FCOE_PRIORITY,
		DEFINE_DCB_PROPRITY_PROPERTY_TYPE
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FCOE_MODE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_DCB_FCOE_MODE_FABRIC,
			                                           NM_SETTING_DCB_FCOE_MODE_VN2VN),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_ISCSI_FLAGS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_app_iscsi_flags,
			.set_fcn =                  _set_fcn_dcb_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_ISCSI_PRIORITY,
		DEFINE_DCB_PROPRITY_PROPERTY_TYPE
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FIP_FLAGS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_app_fip_flags,
			.set_fcn =                  _set_fcn_dcb_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FIP_PRIORITY,
		DEFINE_DCB_PROPRITY_PROPERTY_TYPE
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_flow_control_flags,
			.set_fcn =                  _set_fcn_dcb_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_flow_control,
			.set_fcn =                  _set_fcn_dcb_priority_flow_control,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_group_flags,
			.set_fcn =                  _set_fcn_dcb_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_GROUP_ID,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_group_id,
			.set_fcn =                  _set_fcn_dcb_priority_group_id,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_group_bandwidth,
			.set_fcn =                  _set_fcn_dcb_priority_group_bandwidth,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_BANDWIDTH,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_bandwidth,
			.set_fcn =                  _set_fcn_dcb_priority_bandwidth,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_strict,
			.set_fcn =                  _set_fcn_dcb_priority_strict,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_dcb_priority_traffic_class,
			.set_fcn =                  _set_fcn_dcb_priority_traffic_class,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM
static const NMMetaPropertyInfo *const property_infos_GSM[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_NUMBER,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_USERNAME,
		.is_cli_option =                TRUE,
		.property_alias =               "user",
		.prompt =                       N_("Username [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_PASSWORD,
		.is_cli_option =                TRUE,
		.property_alias =               "password",
		.prompt =                       N_("Password [none]"),
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_APN,
		.is_cli_option =                TRUE,
		.property_alias =               "apn",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("APN"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_NETWORK_ID,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_PIN,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_PIN_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_HOME_ONLY,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_DEVICE_ID,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_SIM_ID,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_SIM_OPERATOR_ID,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_gsm_sim_operator_id,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_MTU,
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingGsm, nm_setting_gsm_get_mtu),
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_INFINIBAND
static const NMMetaPropertyInfo *const property_infos_INFINIBAND[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "mac",
		.prompt =                       N_("MAC [none]"),
		.property_type =                &_pt_gobject_mac,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mac,
			.mode =                     NM_META_PROPERTY_TYPE_MAC_MODE_INFINIBAND,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_MTU,
		.is_cli_option =                TRUE,
		.property_alias =               "mtu",
		.prompt =                       N_("MTU [auto]"),
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingInfiniband, nm_setting_infiniband_get_mtu),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_TRANSPORT_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "transport-mode",
		.prompt =                       NM_META_TEXT_PROMPT_IB_MODE,
		.def_hint =                     NM_META_TEXT_PROMPT_IB_MODE_CHOICES,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("datagram", "connected"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_P_KEY,
		.is_cli_option =                TRUE,
		.property_alias =               "p-key",
		.prompt =                       N_("P_KEY [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_infiniband_p_key,
			.set_fcn =                  _set_fcn_infiniband_p_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "parent",
		.prompt =                       N_("Parent interface [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_gobject_ifname,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_IP4_CONFIG
static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_METHOD, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_METHOD,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip4_config_method,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            ipv4_valid_methods,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS,
		.describe_message =
		    N_("Enter a list of IPv4 addresses of DNS servers.\n\n"
		       "Example: 8.8.8.8, 8.8.4.4\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip4_config_dns,
			.remove_fcn =               _remove_fcn_ipv4_config_dns,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_SEARCH, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip4_config_dns_search,
			.remove_fcn =               _remove_fcn_ipv4_config_dns_search,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_OPTIONS, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_nmc_with_default,
			.set_fcn =                  _set_fcn_ip4_config_dns_options,
			.remove_fcn =               _remove_fcn_ipv4_config_dns_options,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (get_with_default,
			.fcn =                      GET_FCN_WITH_DEFAULT (NMSettingIPConfig, _dns_options_is_default),
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_PRIORITY, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ADDRESSES, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES,
		.is_cli_option =                TRUE,
		.property_alias =               "ip4",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_MULTI,
		.prompt =                       N_("IPv4 address (IP[/plen]) [none]"),
		.describe_message =
		    N_("Enter a list of IPv4 addresses formatted as:\n"
		       "  ip[/prefix], ip[/prefix],...\n"
		       "Missing prefix is regarded as prefix of 32.\n\n"
		       "Example: 192.168.1.5/24, 10.0.0.11/24\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_ip_config_addresses,
			.set_fcn =                  _set_fcn_ip4_config_addresses,
			.remove_fcn =               _remove_fcn_ipv4_config_addresses,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_GATEWAY, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_GATEWAY,
		.is_cli_option =                TRUE,
		.property_alias =               "gw4",
		.prompt =                       N_("IPv4 gateway [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip4_config_gateway,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTES, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES,
		.describe_message =
		    N_("Enter a list of IPv4 routes formatted as:\n"
		       "  ip[/prefix] [next-hop] [metric],...\n\n"
		       "Missing prefix is regarded as a prefix of 32.\n"
		       "Missing next-hop is regarded as 0.0.0.0.\n"
		       "Missing metric means default (NM/kernel will set a default value).\n\n"
		       "Examples: 192.168.2.0/24 192.168.2.1 3, 10.1.0.0/16 10.0.0.254\n"
		       "          10.1.2.0/24\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_ip_config_routes,
			.set_fcn =                  _set_fcn_ip4_config_routes,
			.remove_fcn =               _remove_fcn_ipv4_config_routes,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_METRIC, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_METRIC,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_TABLE, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_TABLE,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos = INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "unspec",
				},
				{
					.value = 254,
					.nick = "main",
				}
			),
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				},
				{
					.value = G_MAXINT32,
					.nick = "infinity",
				}
			),
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP4_CONFIG_DHCP_FQDN,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_NEVER_DEFAULT,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_MAY_FAIL, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_MAY_FAIL,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DAD_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =              INT_VALUE_INFOS (
				{
					.value = -1,
					.nick = "default",
				},
				{
					.value = 0,
					.nick = "off",
				}
			),
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_IP6_CONFIG
static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_METHOD, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_METHOD,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip6_config_method,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            ipv6_valid_methods,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS,
		.describe_message =
		    N_("Enter a list of IPv6 addresses of DNS servers.  If the IPv6 "
		       "configuration method is 'auto' these DNS servers are appended "
		       "to those (if any) returned by automatic configuration.  DNS "
		       "servers cannot be used with the 'shared' or 'link-local' IPv6 "
		       "configuration methods, as there is no upstream network. In "
		       "all other IPv6 configuration methods, these DNS "
		       "servers are used as the only DNS servers for this connection.\n\n"
		       "Example: 2607:f0d0:1002:51::4, 2607:f0d0:1002:51::1\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip6_config_dns,
			.remove_fcn =               _remove_fcn_ipv6_config_dns,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_SEARCH, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip6_config_dns_search,
			.remove_fcn =               _remove_fcn_ipv6_config_dns_search,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_OPTIONS, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_nmc_with_default,
			.set_fcn =                  _set_fcn_ip6_config_dns_options,
			.remove_fcn =               _remove_fcn_ipv6_config_dns_options,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (get_with_default,
			.fcn =     GET_FCN_WITH_DEFAULT (NMSettingIPConfig, _dns_options_is_default),
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_PRIORITY, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ADDRESSES, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES,
		.is_cli_option =                TRUE,
		.property_alias =               "ip6",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_MULTI,
		.prompt =                       N_("IPv6 address (IP[/plen]) [none]"),
		.describe_message =
		    N_("Enter a list of IPv6 addresses formatted as:\n"
		       "  ip[/prefix], ip[/prefix],...\n"
		       "Missing prefix is regarded as prefix of 128.\n\n"
		       "Example: 2607:f0d0:1002:51::4/64, 1050:0:0:0:5:600:300c:326b\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_ip_config_addresses,
			.set_fcn =                  _set_fcn_ip6_config_addresses,
			.remove_fcn =               _remove_fcn_ipv6_config_addresses,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_GATEWAY, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY,
		.is_cli_option =                TRUE,
		.property_alias =               "gw6",
		.prompt =                       N_("IPv6 gateway [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_ip6_config_gateway,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTES, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTES,
		.describe_message =
		    N_("Enter a list of IPv6 routes formatted as:\n"
		       "  ip[/prefix] [next-hop] [metric],...\n\n"
		       "Missing prefix is regarded as a prefix of 128.\n"
		       "Missing next-hop is regarded as \"::\".\n"
		       "Missing metric means default (NM/kernel will set a default value).\n\n"
		       "Examples: 2001:db8:beef:2::/64 2001:db8:beef::2, 2001:db8:beef:3::/64 2001:db8:beef::3 2\n"
		       "          abbe::/64 55\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_ip_config_routes,
			.set_fcn =                  _set_fcn_ip6_config_routes,
			.remove_fcn =               _remove_fcn_ipv6_config_routes,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_METRIC, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_METRIC,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_TABLE, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "unspec",
				},
				{
					.value = 254,
					.nick = "main",
				}
			),
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_NEVER_DEFAULT,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_MAY_FAIL, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MAY_FAIL,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_IP6_PRIVACY,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_ip6_config_ip6_privacy,
			.set_fcn =                  _set_fcn_ip6_config_ip6_privacy,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_ip6_config_addr_gen_mode_get_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_HOSTNAME,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_TOKEN,
		.property_type =                &_pt_gobject_string,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_IP_TUNNEL
static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "mode",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_IP_TUNNEL_MODE,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_ip_tunnel_mode_get_type,
				.min =                  NM_IP_TUNNEL_MODE_UNKNOWN + 1,
				.max =                  G_MAXINT,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "dev",
		.prompt =                       N_("Parent device [none]"),
		.property_type =                &_pt_gobject_devices,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_LOCAL,
		.is_cli_option =                TRUE,
		.property_alias =               "local",
		.prompt =                       N_("Local endpoint [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_REMOTE,
		.is_cli_option =                TRUE,
		.property_alias =               "remote",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("Remote"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_TTL,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_TOS,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_INPUT_KEY,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_OUTPUT_KEY,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_FLOW_LABEL,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_MTU,
		.property_type =                &_pt_gobject_mtu,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_MACSEC
static const NMMetaPropertyInfo *const property_infos_MACSEC[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "dev",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("MACsec parent device or connection UUID"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "mode",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_MACSEC_MODE,
		.def_hint =                     NM_META_TEXT_PROMPT_MACSEC_MODE_CHOICES,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_macsec_mode_get_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_ENCRYPT,
		.is_cli_option =                TRUE,
		.property_alias =               "encrypt",
		.prompt =                       N_("Enable encryption [yes]"),
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_MKA_CAK,
		.is_cli_option =                TRUE,
		.property_alias =               "cak",
		.prompt =                       N_("MKA CAK"),
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_MKA_CAK_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_MKA_CKN,
		.is_cli_option =                TRUE,
		.property_alias =               "ckn",
		.prompt =                       N_("MKA_CKN"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_PORT,
		.is_cli_option =                TRUE,
		.property_alias =               "port",
		.prompt =                       N_("SCI port [1]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACSEC_VALIDATION,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_macsec_validation_get_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_MACVLAN
static const NMMetaPropertyInfo *const property_infos_MACVLAN[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACVLAN_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "dev",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("MACVLAN parent device or connection UUID"),
		.property_type =                &_pt_gobject_devices,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACVLAN_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "mode",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_MACVLAN_MODE,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
			.get_gtype =                nm_setting_macvlan_mode_get_type,
			.min =                      NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
			.max =                      G_MAXINT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACVLAN_PROMISCUOUS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_MACVLAN_TAP,
		.is_cli_option =                TRUE,
		.property_alias =               "tap",
		.prompt =                       N_("Tap [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OLPC_MESH
static const NMMetaPropertyInfo *const property_infos_OLPC_MESH[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OLPC_MESH_SSID,
		.is_cli_option =                TRUE,
		.property_alias =               "ssid",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("SSID"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_olpc_mesh_ssid,
			.set_fcn =                  _set_fcn_gobject_ssid,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OLPC_MESH_CHANNEL,
		.is_cli_option =                TRUE,
		.property_alias =               "channel",
		.prompt =                       N_("OLPC Mesh channel [1]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_olpc_mesh_channel,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "dhcp-anycast",
		.prompt =                       N_("DHCP anycast MAC address [none]"),
		.property_type =                &_pt_gobject_mac,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPPOE
static const NMMetaPropertyInfo *const property_infos_PPPOE[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "parent",
		.prompt =                       N_("PPPoE parent device"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_SERVICE,
		.is_cli_option =                TRUE,
		.property_alias =               "service",
		.prompt =                       N_("Service [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_USERNAME,
		.is_cli_option =                TRUE,
		.property_alias =               "username",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("PPPoE username"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD,
		.is_cli_option =                TRUE,
		.property_alias =               "password",
		.prompt =                       N_("Password [none]"),
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_BRIDGE
static const NMMetaPropertyInfo *const property_infos_OVS_BRIDGE[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_FAIL_MODE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =        VALUES_STATIC ("secure", "standalone"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_STP_ENABLE,
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_INTERFACE
static const NMMetaPropertyInfo *const property_infos_OVS_INTERFACE[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_INTERFACE_TYPE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =        VALUES_STATIC ("internal", "patch"),
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PATCH
static const NMMetaPropertyInfo *const property_infos_OVS_PATCH[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PATCH_PEER,
		.property_type =                &_pt_gobject_string,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PORT
static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_VLAN_MODE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =        VALUES_STATIC ("access", "native-tagged", "native-untagged", "trunk"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TAG,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_LACP,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =        VALUES_STATIC ("active", "off", "passive"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_MODE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =        VALUES_STATIC ("active-backup", "balance-slb", "balance-tcp"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_UPDELAY,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_DOWNDELAY,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPP
static const NMMetaPropertyInfo *const property_infos_PPP[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NOAUTH,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REFUSE_EAP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REFUSE_PAP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REFUSE_CHAP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REFUSE_MSCHAP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REFUSE_MSCHAPV2,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NOBSDCOMP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NODEFLATE,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NO_VJ_COMP,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REQUIRE_MPPE,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_REQUIRE_MPPE_128,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_MPPE_STATEFUL,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_CRTSCTS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_BAUD,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_MRU,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_MTU,
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingPpp, nm_setting_ppp_get_mtu),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_LCP_ECHO_FAILURE,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_LCP_ECHO_INTERVAL,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PROXY
static const NMMetaPropertyInfo *const property_infos_PROXY[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PROXY_METHOD,
		.is_cli_option =                TRUE,
		.property_alias =               "method",
		.prompt =                       NM_META_TEXT_PROMPT_PROXY_METHOD,
		.def_hint =                     NM_META_TEXT_PROMPT_PROXY_METHOD_CHOICES,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_proxy_method_get_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PROXY_BROWSER_ONLY,
		.is_cli_option =                TRUE,
		.property_alias =               "browser-only",
		.prompt =                       N_("Browser only [no]"),
		.property_type =                &_pt_gobject_bool
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PROXY_PAC_URL,
		.is_cli_option =                TRUE,
		.property_alias =               "pac-url",
		.prompt =                       N_("PAC URL"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_PROXY_PAC_SCRIPT,
		.is_cli_option =                TRUE,
		.property_alias =               "pac-script",
		.prompt =                       N_("PAC script"),
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
			.validate_fcn =             _validate_fcn_proxy_pac_script,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TC_CONFIG
static const NMMetaPropertyInfo *const property_infos_TC_CONFIG[] = {
	PROPERTY_INFO (NM_SETTING_TC_CONFIG_QDISCS, DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_tc_config_qdiscs,
			.set_fcn =                  _set_fcn_tc_config_qdiscs,
			.remove_fcn =               _remove_fcn_tc_config_qdiscs,
		),
	),
	PROPERTY_INFO (NM_SETTING_TC_CONFIG_TFILTERS, DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_tc_config_tfilters,
			.set_fcn =                  _set_fcn_tc_config_tfilters,
			.remove_fcn =               _remove_fcn_tc_config_tfilters,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TEAM
static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_CONFIG,
		.is_cli_option =                TRUE,
		.property_alias =               "config",
		.prompt =                       N_("Team JSON configuration [none]"),
		.describe_message =             TEAM_DESCRIBE_MESSAGE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
			.validate_fcn =             _validate_fcn_team_config,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "disabled",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "disabled",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_BROADCAST,
			                                           NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
			                                           NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
			                                           NM_SETTING_TEAM_RUNNER_LOADBALANCE,
			                                           NM_SETTING_TEAM_RUNNER_LACP),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL,
			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE,
			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_HASH,
		.property_type =  DEFINE_PROPERTY_TYPE (
			.get_fcn =                   _get_fcn_gobject,
			.set_fcn =                   _set_fcn_team_runner_tx_hash,
			.remove_fcn =                _remove_fcn_team_runner_tx_hash,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("basic"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_ACTIVE,
		.property_type =                & _pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_FAST_RATE,
		.property_type =                & _pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_SYS_PRIO,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_MIN_PORTS,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
		        .value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO,
			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO_STABLE,
			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_BANDWIDTH,
			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_COUNT,
			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_LINK_WATCHERS,
		.describe_message =             TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_team_link_watchers,
			.set_fcn =                  _set_fcn_team_link_watchers,
			.remove_fcn =               _remove_fcn_team_link_watchers,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TEAM_PORT
static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_CONFIG,
		.is_cli_option =                TRUE,
		.property_alias =               "config",
		.prompt =                       N_("Team JSON configuration [none]"),
		.describe_message =             TEAM_DESCRIBE_MESSAGE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
			.validate_fcn =             _validate_fcn_team_config,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_QUEUE_ID,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_PRIO,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_STICKY,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_PRIO,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_KEY,
		.property_type =                &_pt_gobject_int,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
			.value_infos =          INT_VALUE_INFOS (
				{
					.value = 0,
					.nick = "default",
				}
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LINK_WATCHERS,
		.describe_message =             TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_team_port_link_watchers,
			.set_fcn =                  _set_fcn_team_port_link_watchers,
			.remove_fcn =               _remove_fcn_team_port_link_watchers,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SERIAL
static const NMMetaPropertyInfo *const property_infos_SERIAL[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BAUD,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BITS,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_PARITY,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_serial_parity_get_type,
				.value_infos =          ENUM_VALUE_INFOS (
					{
						.value = NM_SETTING_SERIAL_PARITY_EVEN,
						.nick = "E",
					},
					{
						.value = NM_SETTING_SERIAL_PARITY_EVEN,
						.nick = "e",
					},
					{
						.value = NM_SETTING_SERIAL_PARITY_ODD,
						.nick = "O",
					},
					{
						.value = NM_SETTING_SERIAL_PARITY_ODD,
						.nick = "o",
					},
					{
						.value = NM_SETTING_SERIAL_PARITY_NONE,
						.nick = "N",
					},
					{
						.value = NM_SETTING_SERIAL_PARITY_NONE,
						.nick = "n",
					}
				),
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_STOPBITS,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_SEND_DELAY,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TUN
static const NMMetaPropertyInfo *const property_infos_TUN[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "mode",
		.prompt =                       NM_META_TEXT_PROMPT_TUN_MODE,
		.def_hint =                     NM_META_TEXT_PROMPT_TUN_MODE_CHOICES,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
			.get_gtype =                nm_setting_tun_mode_get_type,
			.min =                      NM_SETTING_TUN_MODE_UNKNOWN + 1,
			.max =                      G_MAXINT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_OWNER,
		.is_cli_option =                TRUE,
		.property_alias =               "owner",
		.prompt =                       N_("User ID [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_GROUP,
		.is_cli_option =                TRUE,
		.property_alias =               "group",
		.prompt =                       N_("Group ID [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_PI,
		.is_cli_option =                TRUE,
		.property_alias =               "pi",
		.prompt =                       N_("Enable PI [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_VNET_HDR,
		.is_cli_option =                TRUE,
		.property_alias =               "vnet-hdr",
		.prompt =                       N_("Enable VNET header [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_MULTI_QUEUE,
		.is_cli_option =                TRUE,
		.property_alias =               "multi-queue",
		.prompt =                       N_("Enable multi queue [no]"),
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_VLAN
static const NMMetaPropertyInfo *const property_infos_VLAN[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "dev",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("VLAN parent device or connection UUID"),
		.property_type =                &_pt_gobject_devices,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_ID,
		.is_cli_option =                TRUE,
		.property_alias =               "id",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("VLAN ID (<0-4094>)"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_FLAGS,
		.is_cli_option =                TRUE,
		.property_alias =               "flags",
		.prompt =                       N_("VLAN flags (<0-7>) [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_vlan_flags,
			.set_fcn =                  _set_fcn_gobject_flags,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP,
		.is_cli_option =                TRUE,
		.property_alias =               "ingress",
		.prompt =                       N_("Ingress priority maps [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_vlan_ingress_priority_map,
			.set_fcn =                  _set_fcn_vlan_ingress_priority_map,
			.remove_fcn =               _remove_fcn_vlan_ingress_priority_map,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP,
		.is_cli_option =                TRUE,
		.property_alias =               "egress",
		.prompt =                       N_("Egress priority maps [none]"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_vlan_egress_priority_map,
			.set_fcn =                  _set_fcn_vlan_egress_priority_map,
			.remove_fcn =               _remove_fcn_vlan_egress_priority_map,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_VPN
static const NMMetaPropertyInfo *const property_infos_VPN[] = {
[_NM_META_PROPERTY_TYPE_VPN_SERVICE_TYPE] =
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_SERVICE_TYPE,
		.is_cli_option =                TRUE,
		.property_alias =               "vpn-type",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       NM_META_TEXT_PROMPT_VPN_TYPE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_vpn_service_type,
			.complete_fcn =             _complete_fcn_vpn_service_type,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_USER_NAME,
		.is_cli_option =                TRUE,
		.property_alias =               "user",
		.prompt =                       N_("Username [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_DATA,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_vpn_data,
			.set_fcn =                  _set_fcn_vpn_data,
			.remove_fcn =               _remove_fcn_vpn_data,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_SECRETS,
		.is_secret =                    TRUE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_vpn_secrets,
			.set_fcn =                  _set_fcn_vpn_secrets,
			.remove_fcn =               _remove_fcn_vpn_secrets,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_PERSISTENT,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VPN_TIMEOUT,
		.property_type =                &_pt_gobject_int,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_VXLAN
static const NMMetaPropertyInfo *const property_infos_VXLAN[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_PARENT,
		.is_cli_option =                TRUE,
		.property_alias =               "dev",
		.prompt =                       N_("Parent device [none]"),
		.property_type =                &_pt_gobject_devices,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_ID,
		.is_cli_option =                TRUE,
		.property_alias =               "id",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("VXLAN ID"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_LOCAL,
		.is_cli_option =                TRUE,
		.property_alias =               "local",
		.prompt =                       N_("Local address [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_REMOTE,
		.is_cli_option =                TRUE,
		.property_alias =               "remote",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("Remote"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_SOURCE_PORT_MIN,
		.is_cli_option =                TRUE,
		.property_alias =               "source-port-min",
		.prompt =                       N_("Minimum source port [0]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_SOURCE_PORT_MAX,
		.is_cli_option =                TRUE,
		.property_alias =               "source-port-max",
		.prompt =                       N_("Maximum source port [0]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_DESTINATION_PORT,
		.is_cli_option =                TRUE,
		.property_alias =               "destination-port",
		.prompt =                       N_("Destination port [8472]"),
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_TOS,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_TTL,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_AGEING,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_LIMIT,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_LEARNING,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_PROXY,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_RSC,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_L2_MISS,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_VXLAN_L3_MISS,
		.property_type =                &_pt_gobject_bool,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIMAX
static const NMMetaPropertyInfo *const property_infos_WIMAX[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIMAX_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "mac",
		.prompt =                       N_("MAC [none]"),
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIMAX_NETWORK_NAME,
		.is_cli_option =                TRUE,
		.property_alias =               "nsp",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("WiMAX NSP name"),
		.property_type =                &_pt_gobject_mac,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIRED
static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_PORT,
		/* Do not allow setting 'port' for now. It is not implemented in
		 * NM core, nor in ifcfg-rh plugin. Enable this when it gets done.
		 * wired_valid_ports[] = { "tp", "aui", "bnc", "mii", NULL };
		 */
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_SPEED,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_DUPLEX,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("half", "full"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_AUTO_NEGOTIATE,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "mac",
		.prompt =                       N_("MAC [none]"),
		.property_type =                &_pt_gobject_mac,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "cloned-mac",
		.prompt =                       N_("Cloned MAC [none]"),
		.property_type =                &_pt_gobject_mac,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mac,
			.mode =                     NM_META_PROPERTY_TYPE_MAC_MODE_CLONED,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wired_mac_address_blacklist,
			.remove_fcn =               _remove_fcn_wired_mac_address_blacklist,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MTU,
		.is_cli_option =                TRUE,
		.property_alias =               "mtu",
		.prompt =                       N_("MTU [auto]"),
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingWired, nm_setting_wired_get_mtu),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_S390_SUBCHANNELS,
		.describe_message =
		    N_("Enter a list of subchannels (comma or space separated).\n\n"
		       "Example: 0.0.0e20 0.0.0e21 0.0.0e22\n"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wired_s390_subchannels,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_S390_NETTYPE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("qeth", "lcs", "ctc"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_S390_OPTIONS,
		.property_type = DEFINE_PROPERTY_TYPE (
			.describe_fcn =             _describe_fcn_wired_s390_options,
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wired_s390_options,
			.remove_fcn =               _remove_fcn_wired_s390_options,
			.values_fcn =               _values_fcn__wired_s390_options,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_WAKE_ON_LAN,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_wired_wake_on_lan_get_type,
				.value_infos =          ENUM_VALUE_INFOS (
					{
						.value = NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
						.nick = "none",
					},
					{
						.value = NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
						.nick = "disable",
					},
					{
						.value = NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
						.nick = "disabled",
					}
				),
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD,
		.property_type =                &_pt_gobject_mac,
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIRELESS
static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SSID,
		.is_cli_option =                TRUE,
		.property_alias =               "ssid",
		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
		.prompt =                       N_("SSID"),
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_wireless_ssid,
			.set_fcn =                  _set_fcn_gobject_ssid,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MODE,
		.is_cli_option =                TRUE,
		.property_alias =               "mode",
		.prompt =                       NM_META_TEXT_PROMPT_WIFI_MODE,
		.def_hint =                     NM_META_TEXT_PROMPT_WIFI_MODE_CHOICES,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC (NM_SETTING_WIRELESS_MODE_INFRA,
			                                           NM_SETTING_WIRELESS_MODE_ADHOC,
			                                           NM_SETTING_WIRELESS_MODE_AP),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_BAND,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("a", "bg"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_CHANNEL,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wireless_channel,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_BSSID,
		.property_type =                &_pt_gobject_mac,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_RATE,
		/* Do not allow setting 'rate'. It is not implemented in NM core. */
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_TX_POWER,
		/* Do not allow setting 'tx-power'. It is not implemented in NM core. */
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MAC_ADDRESS,
		.property_type =                &_pt_gobject_mac,
		.is_cli_option =                TRUE,
		.property_alias =               "mac",
		.prompt =                       N_("MAC [none]"),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
		.is_cli_option =                TRUE,
		.property_alias =               "cloned-mac",
		.prompt =                       N_("Cloned MAC [none]"),
		.property_type =                &_pt_gobject_mac,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mac,
			.mode =                     NM_META_PROPERTY_TYPE_MAC_MODE_CLONED,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wireless_mac_address_blacklist,
			.remove_fcn =               _remove_fcn_wireless_mac_address_blacklist,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_mac_randomization_get_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MTU,
		.is_cli_option =                TRUE,
		.property_alias =               "mtu",
		.prompt =                       N_("MTU [auto]"),
		.property_type =                &_pt_gobject_mtu,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
			.get_fcn =                  MTU_GET_FCN (NMSettingWireless, nm_setting_wireless_get_mtu),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SEEN_BSSIDS,
		.property_type =                &_pt_gobject_readonly,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_HIDDEN,
		.property_type =                &_pt_gobject_bool,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_POWERSAVE,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_wireless_powersave_get_type,
			),
			.typ_flags =                NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT,
		),
	),
	NULL
};

#undef  _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIRELESS_SECURITY
static const NMMetaPropertyInfo *const property_infos_WIRELESS_SECURITY[] = {
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX,
		.property_type =                &_pt_gobject_int,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            VALUES_STATIC ("open", "shared", "leap"),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PROTO,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wireless_security_proto,
			.remove_fcn =               _remove_fcn_wireless_security_proto,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            wifi_sec_valid_protos,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wireless_security_pairwise,
			.remove_fcn =               _remove_fcn_wireless_security_pairwise,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            wifi_sec_valid_pairwises,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_GROUP,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_gobject,
			.set_fcn =                  _set_fcn_wireless_security_group,
			.remove_fcn =               _remove_fcn_wireless_security_group,
		),
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			.values_static =            wifi_sec_valid_groups,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PMF,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_wireless_security_pmf_get_type,
			),
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
		.is_secret =                    TRUE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_wireless_security_wep_key0,
			.set_fcn =                  _set_fcn_wireless_wep_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
		.is_secret =                    TRUE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_wireless_security_wep_key1,
			.set_fcn =                  _set_fcn_wireless_wep_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
		.is_secret =                    TRUE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_wireless_security_wep_key2,
			.set_fcn =                  _set_fcn_wireless_wep_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3,
		.is_secret =                    TRUE,
		.property_type = DEFINE_PROPERTY_TYPE (
			.get_fcn =                  _get_fcn_wireless_security_wep_key3,
			.set_fcn =                  _set_fcn_wireless_wep_key,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
		.describe_message =
		    N_("Enter the type of WEP keys. The accepted values are: "
		       "0 or unknown, 1 or key, and 2 or passphrase.\n"),
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.pre_set_notify =       _gobject_enum_pre_set_notify_fcn_wireless_security_wep_key_type,
			),
			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PSK,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
			.validate_fcn =             _validate_fcn_wireless_security_psk,
		),
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
		.is_secret =                    TRUE,
		.property_type =                &_pt_gobject_string,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS,
		.property_type =                &_pt_gobject_secret_flags,
	),
	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WPS_METHOD,
		.property_type =                &_pt_gobject_enum,
		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
				.get_gtype =            nm_setting_wireless_security_wps_method_get_type,
			),
		),
	),
	NULL
};

/*****************************************************************************/

static void
_setting_init_fcn_adsl (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		/* Initialize a protocol */
		g_object_set (NM_SETTING_ADSL (setting),
		              NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
		              NULL);
	}
}

static void
_setting_init_fcn_cdma (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		/* Initialize 'number' so that 'cdma' is valid */
		g_object_set (NM_SETTING_CDMA (setting),
		              NM_SETTING_CDMA_NUMBER, "#777",
		              NULL);
	}
}

static void
_setting_init_fcn_gsm (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		/* Initialize 'number' so that 'gsm' is valid */
		g_object_set (NM_SETTING_GSM (setting),
		              NM_SETTING_GSM_NUMBER, "*99#",
		              NULL);
	}
}

static void
_setting_init_fcn_infiniband (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		/* Initialize 'transport-mode' so that 'infiniband' is valid */
		g_object_set (NM_SETTING_INFINIBAND (setting),
		              NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
		              NULL);
	}
}

static void
_setting_init_fcn_ip4_config (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (NM_SETTING_IP_CONFIG (setting),
		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
		              NULL);
	}
}

static void
_setting_init_fcn_ip6_config (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (NM_SETTING_IP_CONFIG (setting),
		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
		              NULL);
	}
}

static void
_setting_init_fcn_olpc_mesh (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (NM_SETTING_OLPC_MESH (setting),
		              NM_SETTING_OLPC_MESH_CHANNEL, 1,
		              NULL);
	}
}

static void
_setting_init_fcn_proxy (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (NM_SETTING_PROXY (setting),
		              NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_NONE,
		              NULL);
	}
}

static void
_setting_init_fcn_tun (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (NM_SETTING_TUN (setting),
		              NM_SETTING_TUN_MODE, NM_SETTING_TUN_MODE_TUN,
		              NULL);
	}
}

static void
_setting_init_fcn_vlan (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		g_object_set (setting,
		              NM_SETTING_VLAN_ID, 1,
		              NULL);
	}
}

static void
_setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
{
	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
		/* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
		 * is regarded as "infrastructure", explicit value makes no doubts.
		 */
		g_object_set (NM_SETTING_WIRELESS (setting),
		              NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
		              NULL);
	}
}

/*****************************************************************************/

#define SETTING_PRETTY_NAME_802_1X              N_("802-1x settings")
#define SETTING_PRETTY_NAME_ADSL                N_("ADSL connection")
#define SETTING_PRETTY_NAME_BLUETOOTH           N_("bluetooth connection")
#define SETTING_PRETTY_NAME_BOND                N_("Bond device")
#define SETTING_PRETTY_NAME_BRIDGE              N_("Bridge device")
#define SETTING_PRETTY_NAME_BRIDGE_PORT         N_("Bridge port")
#define SETTING_PRETTY_NAME_CDMA                N_("CDMA mobile broadband connection")
#define SETTING_PRETTY_NAME_CONNECTION          N_("General settings")
#define SETTING_PRETTY_NAME_DCB                 N_("DCB settings")
#define SETTING_PRETTY_NAME_DUMMY               N_("Dummy settings")
#define SETTING_PRETTY_NAME_GENERIC             N_("Generic settings")
#define SETTING_PRETTY_NAME_GSM                 N_("GSM mobile broadband connection")
#define SETTING_PRETTY_NAME_INFINIBAND          N_("InfiniBand connection")
#define SETTING_PRETTY_NAME_IP4_CONFIG          N_("IPv4 protocol")
#define SETTING_PRETTY_NAME_IP6_CONFIG          N_("IPv6 protocol")
#define SETTING_PRETTY_NAME_IP_TUNNEL           N_("IP-tunnel settings")
#define SETTING_PRETTY_NAME_MACSEC              N_("MACsec connection")
#define SETTING_PRETTY_NAME_MACVLAN             N_("macvlan connection")
#define SETTING_PRETTY_NAME_OLPC_MESH           N_("OLPC Mesh connection")
#define SETTING_PRETTY_NAME_OVS_BRIDGE          N_("OpenVSwitch bridge settings")
#define SETTING_PRETTY_NAME_OVS_INTERFACE       N_("OpenVSwitch interface settings")
#define SETTING_PRETTY_NAME_OVS_PATCH           N_("OpenVSwitch patch interface settings")
#define SETTING_PRETTY_NAME_OVS_PORT            N_("OpenVSwitch port settings")
#define SETTING_PRETTY_NAME_PPP                 N_("PPP settings")
#define SETTING_PRETTY_NAME_PPPOE               N_("PPPoE")
#define SETTING_PRETTY_NAME_PROXY               N_("Proxy")
#define SETTING_PRETTY_NAME_SERIAL              N_("Serial settings")
#define SETTING_PRETTY_NAME_TC_CONFIG           N_("Traffic controls")
#define SETTING_PRETTY_NAME_TEAM                N_("Team device")
#define SETTING_PRETTY_NAME_TEAM_PORT           N_("Team port")
#define SETTING_PRETTY_NAME_TUN                 N_("Tun device")
#define SETTING_PRETTY_NAME_USER                N_("User settings")
#define SETTING_PRETTY_NAME_VLAN                N_("VLAN connection")
#define SETTING_PRETTY_NAME_VPN                 N_("VPN connection")
#define SETTING_PRETTY_NAME_VXLAN               N_("VXLAN connection")
#define SETTING_PRETTY_NAME_WIMAX               N_("WiMAX connection")
#define SETTING_PRETTY_NAME_WIRED               N_("Wired Ethernet")
#define SETTING_PRETTY_NAME_WIRELESS            N_("Wi-Fi connection")
#define SETTING_PRETTY_NAME_WIRELESS_SECURITY   N_("Wi-Fi security settings")

#define NM_META_SETTING_VALID_PARTS(...) \
	((const NMMetaSettingValidPartItem *const[]) { __VA_ARGS__  NULL })

#define NM_META_SETTING_VALID_PART_ITEM(type, mand) \
	(&((const NMMetaSettingValidPartItem) { \
		.setting_info =                     &nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_##type], \
		.mandatory =                        mand, \
	}))

const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
#define SETTING_INFO_EMPTY(type, ...) \
	[NM_META_SETTING_TYPE_##type] = { \
		.meta_type =                        &nm_meta_type_setting_info_editor, \
		.general =                          &nm_meta_setting_infos[NM_META_SETTING_TYPE_##type], \
		.pretty_name =                      SETTING_PRETTY_NAME_##type, \
		__VA_ARGS__ \
	}
#define SETTING_INFO(type, ...) \
	[NM_META_SETTING_TYPE_##type] = { \
		.meta_type =                        &nm_meta_type_setting_info_editor, \
		.general =                          &nm_meta_setting_infos[NM_META_SETTING_TYPE_##type], \
		.properties =                       property_infos_##type, \
		.properties_num =                   G_N_ELEMENTS (property_infos_##type) - 1, \
		.pretty_name =                      SETTING_PRETTY_NAME_##type, \
		__VA_ARGS__ \
	}
	SETTING_INFO (802_1X),
	SETTING_INFO (ADSL,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (ADSL,                  TRUE),
		),
		.setting_init_fcn =             _setting_init_fcn_adsl,
	),
	SETTING_INFO (BLUETOOTH,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (BLUETOOTH,             TRUE),
			NM_META_SETTING_VALID_PART_ITEM (BRIDGE,                FALSE),
			NM_META_SETTING_VALID_PART_ITEM (GSM,                   FALSE),
			NM_META_SETTING_VALID_PART_ITEM (CDMA,                  FALSE),
		),
	),
	SETTING_INFO (BOND,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (BOND,                  TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (BRIDGE,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (BRIDGE,                TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (BRIDGE_PORT),
	SETTING_INFO (CDMA,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (CDMA,                  TRUE),
			NM_META_SETTING_VALID_PART_ITEM (SERIAL,                FALSE),
			NM_META_SETTING_VALID_PART_ITEM (PPP,                   FALSE),
		),
		.setting_init_fcn =             _setting_init_fcn_cdma,
	),
	SETTING_INFO (CONNECTION),
	SETTING_INFO (DCB),
	SETTING_INFO_EMPTY (DUMMY,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (DUMMY,                 TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO_EMPTY (GENERIC,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (GENERIC,               TRUE),
		),
	),
	SETTING_INFO (GSM,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (GSM,                   TRUE),
			NM_META_SETTING_VALID_PART_ITEM (SERIAL,                FALSE),
			NM_META_SETTING_VALID_PART_ITEM (PPP,                   FALSE),
		),
		.setting_init_fcn =             _setting_init_fcn_gsm,
	),
	SETTING_INFO (INFINIBAND,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (INFINIBAND,            TRUE),
		),
		.setting_init_fcn =             _setting_init_fcn_infiniband,
	),
	SETTING_INFO (IP4_CONFIG,
		.setting_init_fcn =             _setting_init_fcn_ip4_config,
	),
	SETTING_INFO (IP6_CONFIG,
		.setting_init_fcn =             _setting_init_fcn_ip6_config,
	),
	SETTING_INFO (IP_TUNNEL,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (IP_TUNNEL,             TRUE),
		),
	),
	SETTING_INFO (MACSEC,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (MACSEC,                TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
			NM_META_SETTING_VALID_PART_ITEM (802_1X,                FALSE),
		),
	),
	SETTING_INFO (MACVLAN,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (MACVLAN,               TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (OLPC_MESH,
		.alias =                            "olpc-mesh",
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (OLPC_MESH,             TRUE),
		),
		.setting_init_fcn =             _setting_init_fcn_olpc_mesh,
	),
	SETTING_INFO (OVS_BRIDGE,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (OVS_BRIDGE,            TRUE),
		),
	),
	SETTING_INFO (OVS_INTERFACE,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE,         TRUE),
			NM_META_SETTING_VALID_PART_ITEM (OVS_PATCH,             FALSE),
			NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG,            FALSE),
			NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG,            FALSE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (OVS_PATCH),
	SETTING_INFO (OVS_PORT,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (OVS_PORT,              TRUE),
		),
	),
	SETTING_INFO (PPPOE,
		/* PPPoE is a base connection type from historical reasons.
		 * See libnm-core/nm-setting.c:_nm_setting_is_base_type()
		 */
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (PPPOE,                 TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 TRUE),
			NM_META_SETTING_VALID_PART_ITEM (PPP,                   FALSE),
			NM_META_SETTING_VALID_PART_ITEM (802_1X,                FALSE),
		),
	),
	SETTING_INFO (PPP),
	SETTING_INFO (PROXY,
		.setting_init_fcn =             _setting_init_fcn_proxy,
	),
	SETTING_INFO (SERIAL),
	SETTING_INFO (TC_CONFIG),
	SETTING_INFO (TEAM,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (TEAM,                  TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (TEAM_PORT),
	SETTING_INFO (TUN,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (TUN,                   TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
		.setting_init_fcn =             _setting_init_fcn_tun,
	),
	SETTING_INFO_EMPTY (USER),
	SETTING_INFO (VLAN,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (VLAN,                  TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
		.setting_init_fcn =             _setting_init_fcn_vlan,
	),
	SETTING_INFO (VPN,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (VPN,                   TRUE),
		),
	),
	SETTING_INFO (VXLAN,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (VXLAN,                 TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
		),
	),
	SETTING_INFO (WIMAX,
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIMAX,                 TRUE),
		),
	),
	SETTING_INFO (WIRED,
		.alias =                            "ethernet",
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRED,                 TRUE),
			NM_META_SETTING_VALID_PART_ITEM (802_1X,                FALSE),
			NM_META_SETTING_VALID_PART_ITEM (DCB,                   FALSE),
		),
	),
	SETTING_INFO (WIRELESS,
		.alias =                            "wifi",
		.valid_parts = NM_META_SETTING_VALID_PARTS (
			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRELESS,              TRUE),
			NM_META_SETTING_VALID_PART_ITEM (WIRELESS_SECURITY,     FALSE),
			NM_META_SETTING_VALID_PART_ITEM (802_1X,                FALSE),
		),
		.setting_init_fcn =             _setting_init_fcn_wireless,
	),
	SETTING_INFO (WIRELESS_SECURITY,
		.alias =                            "wifi-sec",
	),
};

/*****************************************************************************/

const NMMetaSettingValidPartItem *const nm_meta_setting_info_valid_parts_default[] = {
	NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
	NULL
};

/*****************************************************************************/

static const NMMetaSettingValidPartItem *const valid_settings_noslave[] = {
	NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE),
	NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE),
	NM_META_SETTING_VALID_PART_ITEM (TC_CONFIG,  FALSE),
	NM_META_SETTING_VALID_PART_ITEM (PROXY,      FALSE),
	NULL,
};

static const NMMetaSettingValidPartItem *const valid_settings_slave_bridge[] = {
	NM_META_SETTING_VALID_PART_ITEM (BRIDGE_PORT, TRUE),
	NULL,
};

static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_bridge[] = {
	NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, FALSE),
	NULL,
};

static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_port[] = {
	NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE, FALSE),
	NULL,
};

static const NMMetaSettingValidPartItem *const valid_settings_slave_team[] = {
	NM_META_SETTING_VALID_PART_ITEM (TEAM_PORT, TRUE),
	NULL,
};

const NMMetaSettingValidPartItem *const*
nm_meta_setting_info_valid_parts_for_slave_type (const char *slave_type, const char **out_slave_name)
{
	if (!slave_type) {
		NM_SET_OUT (out_slave_name, NULL);
		return valid_settings_noslave;
	}
	if (nm_streq (slave_type, NM_SETTING_BOND_SETTING_NAME)) {
		NM_SET_OUT (out_slave_name, "bond-slave");
		return NM_PTRARRAY_EMPTY (const NMMetaSettingValidPartItem *);
	}
	if (nm_streq (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
		NM_SET_OUT (out_slave_name, "bridge-slave");
		return valid_settings_slave_bridge;
	}
	if (nm_streq (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
		NM_SET_OUT (out_slave_name, "ovs-slave");
		return valid_settings_slave_ovs_bridge;
	}
	if (nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
		NM_SET_OUT (out_slave_name, "ovs-slave");
		return valid_settings_slave_ovs_port;
	}
	if (nm_streq (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
		NM_SET_OUT (out_slave_name, "team-slave");
		return valid_settings_slave_team;
	}
	return NULL;
}

/*****************************************************************************/

static const char *
_meta_type_setting_info_editor_get_name (const NMMetaAbstractInfo *abstract_info, gboolean for_header)
{
	if (for_header)
		return N_("name");
	return ((const NMMetaSettingInfoEditor *) abstract_info)->general->setting_name;
}

static const char *
_meta_type_property_info_get_name (const NMMetaAbstractInfo *abstract_info, gboolean for_header)
{
	return ((const NMMetaPropertyInfo *) abstract_info)->property_name;
}

static gconstpointer
_meta_type_setting_info_editor_get_fcn (const NMMetaAbstractInfo *abstract_info,
                                        const NMMetaEnvironment *environment,
                                        gpointer environment_user_data,
                                        gpointer target,
                                        NMMetaAccessorGetType get_type,
                                        NMMetaAccessorGetFlags get_flags,
                                        NMMetaAccessorGetOutFlags *out_flags,
                                        gpointer *out_to_free)
{
	const NMMetaSettingInfoEditor *info = (const NMMetaSettingInfoEditor *) abstract_info;

	nm_assert (!out_to_free || !*out_to_free);
	nm_assert (out_flags && !*out_flags);

	if (!NM_IN_SET (get_type,
	                NM_META_ACCESSOR_GET_TYPE_PARSABLE,
	                NM_META_ACCESSOR_GET_TYPE_PRETTY))
		return NULL;

	nm_assert (out_to_free);

	return info->general->setting_name;
}

static gconstpointer
_meta_type_property_info_get_fcn (const NMMetaAbstractInfo *abstract_info,
                                  const NMMetaEnvironment *environment,
                                  gpointer environment_user_data,
                                  gpointer target,
                                  NMMetaAccessorGetType get_type,
                                  NMMetaAccessorGetFlags get_flags,
                                  NMMetaAccessorGetOutFlags *out_flags,
                                  gpointer *out_to_free)
{
	const NMMetaPropertyInfo *info = (const NMMetaPropertyInfo *) abstract_info;

	nm_assert (!out_to_free || !*out_to_free);
	nm_assert (out_flags && !*out_flags);

	if (!NM_IN_SET (get_type,
	                NM_META_ACCESSOR_GET_TYPE_PARSABLE,
	                NM_META_ACCESSOR_GET_TYPE_PRETTY))
		return NULL;

	nm_assert (out_to_free);

	if (   info->is_secret
	    && !NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS))
		return _get_text_hidden (get_type);

	return info->property_type->get_fcn (info,
	                                     environment,
	                                     environment_user_data,
	                                     target,
	                                     get_type,
	                                     get_flags,
	                                     out_flags,
	                                     out_to_free);

}

static const NMMetaAbstractInfo *const*
_meta_type_setting_info_editor_get_nested (const NMMetaAbstractInfo *abstract_info,
                                           guint *out_len,
                                           gpointer *out_to_free)
{
	const NMMetaSettingInfoEditor *info;

	info = (const NMMetaSettingInfoEditor *) abstract_info;

	NM_SET_OUT (out_len, info->properties_num);
	*out_to_free = NULL;
	return (const NMMetaAbstractInfo *const*) info->properties;
}

static const NMMetaAbstractInfo *const*
_meta_type_property_info_get_nested (const NMMetaAbstractInfo *abstract_info,
                                     guint *out_len,
                                     gpointer *out_to_free)
{
	NM_SET_OUT (out_len, 0);
	*out_to_free = NULL;
	return NULL;
}

static const char *const*
_meta_type_property_info_complete_fcn (const NMMetaAbstractInfo *abstract_info,
                                       const NMMetaEnvironment *environment,
                                       gpointer environment_user_data,
                                       const NMMetaOperationContext *operation_context,
                                       const char *text,
                                       char ***out_to_free)
{
	const NMMetaPropertyInfo *info = (const NMMetaPropertyInfo *) abstract_info;

	nm_assert (out_to_free && !*out_to_free);

	if (info->property_type->complete_fcn) {
		return info->property_type->complete_fcn (info,
		                                          environment,
		                                          environment_user_data,
		                                          operation_context,
		                                          text,
		                                          out_to_free);
	}

	if (info->property_type->values_fcn) {
		return info->property_type->values_fcn (info,
		                                        out_to_free);
	}

	if (   info->property_typ_data
	    && info->property_typ_data->values_static)
		return info->property_typ_data->values_static;

	return NULL;
}

const NMMetaType nm_meta_type_setting_info_editor = {
	.type_name =         "setting_info_editor",
	.get_name =          _meta_type_setting_info_editor_get_name,
	.get_nested =        _meta_type_setting_info_editor_get_nested,
	.get_fcn =           _meta_type_setting_info_editor_get_fcn,
};

const NMMetaType nm_meta_type_property_info = {
	.type_name =        "property_info",
	.get_name =         _meta_type_property_info_get_name,
	.get_nested =       _meta_type_property_info_get_nested,
	.get_fcn =          _meta_type_property_info_get_fcn,
	.complete_fcn =     _meta_type_property_info_complete_fcn,
};

const NMMetaType nm_meta_type_nested_property_info = {
	.type_name =        "nested_property_info",
};
