* mapping property names to values
* @connection_dict: the #GVariant containing an %NM_VARIANT_TYPE_CONNECTION
* dictionary mapping setting names to dictionaries.
+ * @parse_flags: flags to determine behavior during parsing.
* @error: location to store error, or %NULL
*
* Creates a new #NMSetting object and populates that object with the properties
_nm_setting_new_from_dbus (GType setting_type,
GVariant *setting_dict,
GVariant *connection_dict,
+ NMSettingParseFlags parse_flags,
GError **error)
{
- NMSetting *setting;
+ gs_unref_object NMSetting *setting = NULL;
+ gs_unref_hashtable GHashTable *keys = NULL;
const NMSettingProperty *properties;
- guint n_properties;
- guint i;
+ guint i, n_properties;
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
g_return_val_if_fail (g_variant_is_of_type (setting_dict, NM_VARIANT_TYPE_SETTING), NULL);
+ nm_assert (!NM_FLAGS_ANY (parse_flags, ~NM_SETTING_PARSE_FLAGS_ALL));
+ nm_assert (!NM_FLAGS_ALL (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_BEST_EFFORT));
+
/* connection_dict is not technically optional, but some tests in test-general
* don't bother with it in cases where they know it's not needed.
*/
*/
setting = (NMSetting *) g_object_new (setting_type, NULL);
+ if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
+ GVariantIter iter;
+ GVariant *entry, *entry_key;
+ char *key;
+
+ keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ g_variant_iter_init (&iter, setting_dict);
+ while ((entry = g_variant_iter_next_value (&iter))) {
+ entry_key = g_variant_get_child_value (entry, 0);
+ key = g_strdup (g_variant_get_string (entry_key, NULL));
+ g_variant_unref (entry_key);
+ g_variant_unref (entry);
+
+ if (!nm_g_hash_table_add (keys, key)) {
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
+ _("duplicate property"));
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
+ return NULL;
+ }
+ }
+ }
+
properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
for (i = 0; i < n_properties; i++) {
const NMSettingProperty *property = &properties[i];
- GVariant *value;
+ gs_unref_variant GVariant *value = NULL;
+ gs_free_error GError *local = NULL;
if (property->param_spec && !(property->param_spec->flags & G_PARAM_WRITABLE))
continue;
value = g_variant_lookup_value (setting_dict, property->name, NULL);
+ if (value && keys)
+ g_hash_table_remove (keys, property->name);
+
if (value && property->set_func) {
+
if (!g_variant_type_equal (g_variant_get_type (value), property->dbus_type)) {
- property_type_error:
+ /* for backward behavior, fail unless best-effort is chosen. */
+ if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+ continue;
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("can't set property of type '%s' from value of type '%s'"),
property->dbus_type ?
g_type_name (property->param_spec->value_type) : "(unknown)",
g_variant_get_type_string (value));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
-
- g_variant_unref (value);
- g_object_unref (setting);
return NULL;
}
- property->set_func (setting,
- connection_dict,
- property->name,
- value);
+ if (!property->set_func (setting,
+ connection_dict,
+ property->name,
+ value,
+ parse_flags,
+ &local)) {
+ if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+ continue;
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("failed to set property: %s"),
+ local->message);
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+ return NULL;
+ }
} else if (!value && property->not_set_func) {
- property->not_set_func (setting,
- connection_dict,
- property->name);
+ if (!property->not_set_func (setting,
+ connection_dict,
+ property->name,
+ parse_flags,
+ &local)) {
+ if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+ continue;
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("failed to set property: %s"),
+ local->message);
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+ return NULL;
+ }
} else if (value && property->param_spec) {
- GValue object_value = { 0, };
+ nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
g_value_init (&object_value, property->param_spec->value_type);
- if (!set_property_from_dbus (property, value, &object_value))
- goto property_type_error;
+ if (!set_property_from_dbus (property, value, &object_value)) {
+ /* for backward behavior, fail unless best-effort is chosen. */
+ if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+ continue;
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("can't set property of type '%s' from value of type '%s'"),
+ property->dbus_type ?
+ g_variant_type_peek_string (property->dbus_type) :
+ property->param_spec ?
+ g_type_name (property->param_spec->value_type) : "(unknown)",
+ g_variant_get_type_string (value));
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+ return NULL;
+ }
- g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value);
- g_value_unset (&object_value);
+ if (!nm_g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value, &local)) {
+ if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+ continue;
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("can not set property: %s"),
+ local->message);
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+ return NULL;
+ }
}
+ }
- if (value)
- g_variant_unref (value);
+ if ( NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)
+ && g_hash_table_size (keys) > 0) {
+ GHashTableIter iter;
+ const char *key;
+
+ g_hash_table_iter_init (&iter, keys);
+ if (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+ g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("unknown property"));
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
+ return NULL;
+ }
}
- return setting;
+ return nm_unauto (&setting);
}
/**