Blame libnm-core/nm-setting-user.c

Packit Service 87a54e
/* SPDX-License-Identifier: LGPL-2.1-or-later */
Packit 5756e2
/*
Packit 5756e2
 * Copyright (C) 2017 Red Hat, Inc.
Packit 5756e2
 */
Packit 5756e2
Packit Service 2bceb2
#include "libnm-core/nm-default-libnm-core.h"
Packit 5756e2
Packit 5756e2
#include "nm-setting-user.h"
Packit 5756e2
Packit 5756e2
#include "nm-setting.h"
Packit 5756e2
#include "nm-setting-private.h"
Packit 5756e2
#include "nm-utils-private.h"
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * SECTION:nm-setting-user
Packit 5756e2
 * @short_description: Describes user properties
Packit 5756e2
 *
Packit 5756e2
 * The #NMSettingUser object is a #NMSetting subclass that allow to attach
Packit 5756e2
 * arbitrary user data to #NMConnection objects.
Packit 5756e2
 **/
Packit 5756e2
Packit 5756e2
#define MAX_NUM_KEYS 256
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit Service a1bd4f
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingUser, PROP_DATA, );
Packit 5756e2
Packit 5756e2
typedef struct {
Packit Service a1bd4f
    GHashTable * data;
Packit Service a1bd4f
    GHashTable * data_invalid;
Packit Service a1bd4f
    const char **keys;
Packit 5756e2
} NMSettingUserPrivate;
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * NMSettingUser:
Packit 5756e2
 *
Packit 5756e2
 * General User Profile Settings
Packit 5756e2
 */
Packit 5756e2
struct _NMSettingUser {
Packit Service a1bd4f
    NMSetting            parent;
Packit Service a1bd4f
    NMSettingUserPrivate _priv;
Packit 5756e2
};
Packit 5756e2
Packit 5756e2
struct _NMSettingUserClass {
Packit Service a1bd4f
    NMSettingClass parent;
Packit 5756e2
};
Packit 5756e2
Packit Service a1bd4f
G_DEFINE_TYPE(NMSettingUser, nm_setting_user, NM_TYPE_SETTING)
Packit 5756e2
Packit 5756e2
#define NM_SETTING_USER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMSettingUser, NM_IS_SETTING_USER)
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
_key_char_is_regular(char ch)
Packit 5756e2
{
Packit Service a1bd4f
    /* allow words of printable characters, plus some
Packit Service a1bd4f
     * special characters, for example to support base64 encoding. */
Packit Service a1bd4f
    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
Packit Service a1bd4f
           || NM_IN_SET(ch, '-', '_', '+', '/', '=');
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_check_key:
Packit 5756e2
 * @key: the key to check
Packit 5756e2
 * @error: a #GError, %NULL to ignore.
Packit 5756e2
 *
Packit 5756e2
 * Checks whether @key is a valid user data key. This means,
Packit 5756e2
 * key is not %NULL, not too large and valid ASCII. Also,
Packit 5756e2
 * only digits and numbers are allowed with a few special
Packit 5756e2
 * characters. The key must contain at least one '.' and
Packit 5756e2
 * look like a fully qualified DNS name.
Packit 5756e2
 *
Packit 5756e2
 * Since: 1.8
Packit 5756e2
 *
Packit 5756e2
 * Returns: %TRUE if @key is a valid user data key.
Packit 5756e2
 */
Packit 5756e2
gboolean
Packit Service a1bd4f
nm_setting_user_check_key(const char *key, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    gsize    len;
Packit Service a1bd4f
    gboolean has_dot;
Packit Service a1bd4f
    char     ch;
Packit Service a1bd4f
Packit Service a1bd4f
    g_return_val_if_fail(!error || !*error, FALSE);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!key || !key[0]) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("missing key"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
    len = strlen(key);
Packit Service a1bd4f
    if (len > 255) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("key is too long"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
    if (!g_utf8_validate(key, len, NULL)) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("key must be UTF8"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    has_dot = FALSE;
Packit Service a1bd4f
    while (TRUE) {
Packit Service a1bd4f
        ch = (key++)[0];
Packit Service a1bd4f
Packit Service a1bd4f
        /* Allow something that looks like a FQN, separating namespaces by a single '.'
Packit Service a1bd4f
         * We want to print the keys nicely in nmcli requiring escaping.
Packit Service a1bd4f
         *
Packit Service a1bd4f
         * If a user really has to encode special values in the name, he may base64 encode it. */
Packit Service a1bd4f
Packit Service a1bd4f
        if (!_key_char_is_regular(ch))
Packit Service a1bd4f
            break;
Packit Service a1bd4f
Packit Service a1bd4f
        while (_key_char_is_regular(key[0]))
Packit Service a1bd4f
            key++;
Packit Service a1bd4f
Packit Service a1bd4f
        ch = key[0];
Packit Service a1bd4f
        if (ch == '\0') {
Packit Service a1bd4f
            if (!has_dot) {
Packit Service a1bd4f
                g_set_error_literal(error,
Packit Service a1bd4f
                                    NM_CONNECTION_ERROR,
Packit Service a1bd4f
                                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                                    _("key requires a '.' for a namespace"));
Packit Service a1bd4f
                return FALSE;
Packit Service a1bd4f
            }
Packit Service a1bd4f
            return TRUE;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (ch != '.')
Packit Service a1bd4f
            break;
Packit Service a1bd4f
Packit Service a1bd4f
        has_dot = TRUE;
Packit Service a1bd4f
        ch      = (++key)[0];
Packit Service a1bd4f
        if (ch == '.') {
Packit Service a1bd4f
            g_set_error_literal(error,
Packit Service a1bd4f
                                NM_CONNECTION_ERROR,
Packit Service a1bd4f
                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                                _("key cannot contain \"..\""));
Packit Service a1bd4f
            return FALSE;
Packit Service a1bd4f
        }
Packit Service a1bd4f
    }
Packit Service a1bd4f
    g_set_error_literal(error,
Packit Service a1bd4f
                        NM_CONNECTION_ERROR,
Packit Service a1bd4f
                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                        _("key contains invalid characters"));
Packit Service a1bd4f
    return FALSE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_check_val:
Packit 5756e2
 * @val: the value to check
Packit 5756e2
 * @error: a #GError, %NULL to ignore.
Packit 5756e2
 *
Packit 5756e2
 * Checks whether @val is a valid user data value. This means,
Packit 5756e2
 * value is not %NULL, not too large and valid UTF-8.
Packit 5756e2
 *
Packit 5756e2
 * Since: 1.8
Packit 5756e2
 *
Packit 5756e2
 * Returns: %TRUE if @val is a valid user data value.
Packit 5756e2
 */
Packit 5756e2
gboolean
Packit Service a1bd4f
nm_setting_user_check_val(const char *val, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    gsize len;
Packit Service a1bd4f
Packit Service a1bd4f
    g_return_val_if_fail(!error || !*error, FALSE);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!val) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("value is missing"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    len = strlen(val);
Packit Service a1bd4f
    if (len > 8 * 1024) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("value is too large"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (!g_utf8_validate(val, len, NULL)) {
Packit Service a1bd4f
        g_set_error_literal(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                            _("value is not valid UTF8"));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static GHashTable *
Packit Service a1bd4f
_create_data_hash(void)
Packit 5756e2
{
Packit Service a1bd4f
    return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_get_keys:
Packit 5756e2
 * @setting: the #NMSettingUser
Packit 5756e2
 * @out_len: (out): the length of the returned array
Packit 5756e2
 *
Packit 5756e2
 * Returns: (array length=out_len) (transfer none): a
Packit 5756e2
 *   %NULL-terminated array containing each key from the table.
Packit 5756e2
  **/
Packit Service a1bd4f
const char *const *
Packit Service a1bd4f
nm_setting_user_get_keys(NMSettingUser *setting, guint *out_len)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = setting;
Packit Service a1bd4f
    NMSettingUserPrivate *priv;
Packit 5756e2
Packit Service a1bd4f
    g_return_val_if_fail(NM_IS_SETTING_USER(self), NULL);
Packit 5756e2
Packit Service a1bd4f
    priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit 5756e2
Packit Service a1bd4f
    if (priv->keys) {
Packit Service a1bd4f
        NM_SET_OUT(out_len, g_hash_table_size(priv->data));
Packit Service a1bd4f
        return priv->keys;
Packit Service a1bd4f
    }
Packit 5756e2
Packit Service a1bd4f
    priv->keys = nm_utils_strdict_get_keys(priv->data, TRUE, out_len);
Packit 5756e2
Packit Service a1bd4f
    /* don't return %NULL, but hijack the @keys fields as a pseudo
Packit Service a1bd4f
     * empty strv array. */
Packit Service a1bd4f
    return priv->keys ?: ((const char **) &priv->keys);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_get_data:
Packit 5756e2
 * @setting: the #NMSettingUser instance
Packit 5756e2
 * @key: the key to lookup
Packit 5756e2
 *
Packit 5756e2
 * Since: 1.8
Packit 5756e2
 *
Packit 5756e2
 * Returns: (transfer none): the value associated with @key or %NULL if no such
Packit 5756e2
 *   value exists.
Packit 5756e2
 */
Packit 5756e2
const char *
Packit Service a1bd4f
nm_setting_user_get_data(NMSettingUser *setting, const char *key)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = setting;
Packit Service a1bd4f
    NMSettingUserPrivate *priv;
Packit 5756e2
Packit Service a1bd4f
    g_return_val_if_fail(NM_IS_SETTING_USER(self), NULL);
Packit Service a1bd4f
    g_return_val_if_fail(key, NULL);
Packit 5756e2
Packit Service a1bd4f
    priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit 5756e2
Packit Service a1bd4f
    if (!priv->data)
Packit Service a1bd4f
        return NULL;
Packit 5756e2
Packit Service a1bd4f
    return g_hash_table_lookup(priv->data, key);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_set_data:
Packit 5756e2
 * @setting: the #NMSettingUser instance
Packit 5756e2
 * @key: the key to set
Packit 5756e2
 * @val: (allow-none): the value to set or %NULL to clear a key.
Packit 5756e2
 * @error: (allow-none): optional error argument
Packit 5756e2
 *
Packit 5756e2
 * Since: 1.8
Packit 5756e2
 *
Packit 5756e2
 * Returns: %TRUE if the operation was successful. The operation
Packit 5756e2
 *   can fail if @key or @val are not valid strings according
Packit 5756e2
 *   to nm_setting_user_check_key() and nm_setting_user_check_val().
Packit 5756e2
 */
Packit 5756e2
gboolean
Packit Service a1bd4f
nm_setting_user_set_data(NMSettingUser *setting, const char *key, const char *val, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = setting;
Packit Service a1bd4f
    NMSettingUserPrivate *priv;
Packit Service a1bd4f
    gboolean              changed = FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    g_return_val_if_fail(NM_IS_SETTING_USER(self), FALSE);
Packit Service a1bd4f
    g_return_val_if_fail(!error || !*error, FALSE);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!nm_setting_user_check_key(key, error))
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    if (val && !nm_setting_user_check_val(val, error))
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!val) {
Packit Service a1bd4f
        if (priv->data && g_hash_table_remove(priv->data, key)) {
Packit Service a1bd4f
            nm_clear_g_free(&priv->keys);
Packit Service a1bd4f
            changed = TRUE;
Packit Service a1bd4f
        }
Packit Service a1bd4f
        goto out;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (priv->data) {
Packit Service a1bd4f
        const char *key2, *val2;
Packit Service a1bd4f
Packit Service a1bd4f
        if (g_hash_table_lookup_extended(priv->data, key, (gpointer *) &key2, (gpointer *) &val2)) {
Packit Service a1bd4f
            if (nm_streq(val, val2))
Packit Service a1bd4f
                goto out;
Packit Service a1bd4f
        } else {
Packit Service a1bd4f
            if (g_hash_table_size(priv->data) >= MAX_NUM_KEYS) {
Packit Service a1bd4f
                /* limit the number of valid keys */
Packit Service a1bd4f
                g_set_error_literal(error,
Packit Service a1bd4f
                                    NM_CONNECTION_ERROR,
Packit Service a1bd4f
                                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                                    _("maximum number of user data entries reached"));
Packit Service a1bd4f
                return FALSE;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            nm_clear_g_free(&priv->keys);
Packit Service a1bd4f
        }
Packit Service a1bd4f
    } else
Packit Service a1bd4f
        priv->data = _create_data_hash();
Packit Service a1bd4f
Packit Service a1bd4f
    g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
Packit Service a1bd4f
    changed = TRUE;
Packit 5756e2
Packit 5756e2
out:
Packit Service a1bd4f
    if (priv->data_invalid) {
Packit Service a1bd4f
        /* setting a value purges all invalid values that were set
Packit Service a1bd4f
         * via GObject property. */
Packit Service a1bd4f
        changed = TRUE;
Packit Service a1bd4f
        nm_clear_pointer(&priv->data_invalid, g_hash_table_unref);
Packit Service a1bd4f
    }
Packit Service a1bd4f
    if (changed)
Packit Service a1bd4f
        _notify(self, PROP_DATA);
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
verify(NMSetting *setting, NMConnection *connection, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = NM_SETTING_USER(setting);
Packit Service a1bd4f
    NMSettingUserPrivate *priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit Service a1bd4f
Packit Service a1bd4f
    if (priv->data_invalid) {
Packit Service a1bd4f
        const char *   key, *val;
Packit Service a1bd4f
        GHashTableIter iter;
Packit Service a1bd4f
        gs_free_error GError *local = NULL;
Packit Service a1bd4f
Packit Service a1bd4f
        g_hash_table_iter_init(&iter, priv->data_invalid);
Packit Service a1bd4f
        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
Packit Service a1bd4f
            if (!nm_setting_user_check_key(key, &local)) {
Packit Service a1bd4f
                g_set_error(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_FAILED,
Packit Service a1bd4f
                            _("invalid key \"%s\": %s"),
Packit Service a1bd4f
                            key,
Packit Service a1bd4f
                            local->message);
Packit Service a1bd4f
            } else if (!nm_setting_user_check_val(val, &local)) {
Packit Service a1bd4f
                g_set_error(error,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR,
Packit Service a1bd4f
                            NM_CONNECTION_ERROR_FAILED,
Packit Service a1bd4f
                            _("invalid value for \"%s\": %s"),
Packit Service a1bd4f
                            key,
Packit Service a1bd4f
                            local->message);
Packit Service a1bd4f
            } else {
Packit Service a1bd4f
                nm_assert_not_reached();
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
            g_prefix_error(error, "%s.%s: ", NM_SETTING_USER_SETTING_NAME, NM_SETTING_USER_DATA);
Packit Service a1bd4f
            return FALSE;
Packit Service a1bd4f
        }
Packit Service a1bd4f
        nm_assert_not_reached();
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) {
Packit Service a1bd4f
        g_set_error(error,
Packit Service a1bd4f
                    NM_CONNECTION_ERROR,
Packit Service a1bd4f
                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
Packit Service a1bd4f
                    _("maximum number of user data entries reached (%u instead of %u)"),
Packit Service a1bd4f
                    g_hash_table_size(priv->data),
Packit Service a1bd4f
                    (unsigned) MAX_NUM_KEYS);
Packit Service a1bd4f
        g_prefix_error(error, "%s.%s: ", NM_SETTING_USER_SETTING_NAME, NM_SETTING_USER_DATA);
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static NMTernary
Packit Service a1bd4f
compare_property(const NMSettInfoSetting *sett_info,
Packit Service a1bd4f
                 guint                    property_idx,
Packit Service a1bd4f
                 NMConnection *           con_a,
Packit Service a1bd4f
                 NMSetting *              set_a,
Packit Service a1bd4f
                 NMConnection *           con_b,
Packit Service a1bd4f
                 NMSetting *              set_b,
Packit Service a1bd4f
                 NMSettingCompareFlags    flags)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUserPrivate *priv, *pri2;
Packit Service a1bd4f
Packit Service a1bd4f
    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_USER_DATA)) {
Packit Service a1bd4f
        if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
Packit Service a1bd4f
            return NM_TERNARY_DEFAULT;
Packit Service a1bd4f
Packit Service a1bd4f
        if (!set_b)
Packit Service a1bd4f
            return TRUE;
Packit Service a1bd4f
Packit Service a1bd4f
        priv = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_a));
Packit Service a1bd4f
        pri2 = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_b));
Packit Service a1bd4f
        return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal)
Packit Service a1bd4f
               && nm_utils_hashtable_equal(priv->data_invalid,
Packit Service a1bd4f
                                           pri2->data_invalid,
Packit Service a1bd4f
                                           TRUE,
Packit Service a1bd4f
                                           g_str_equal);
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return NM_SETTING_CLASS(nm_setting_user_parent_class)
Packit Service a1bd4f
        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static void
Packit Service a1bd4f
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = NM_SETTING_USER(object);
Packit Service a1bd4f
    NMSettingUserPrivate *priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit Service a1bd4f
    GHashTableIter        iter;
Packit Service a1bd4f
    GHashTable *          data;
Packit Service a1bd4f
    const char *          key, *val;
Packit Service a1bd4f
Packit Service a1bd4f
    switch (prop_id) {
Packit Service a1bd4f
    case PROP_DATA:
Packit Service a1bd4f
        data = _create_data_hash();
Packit Service a1bd4f
        if (priv->data) {
Packit Service a1bd4f
            g_hash_table_iter_init(&iter, priv->data);
Packit Service a1bd4f
            while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
Packit Service a1bd4f
                g_hash_table_insert(data, g_strdup(key), g_strdup(val));
Packit Service a1bd4f
        }
Packit Service a1bd4f
        if (priv->data_invalid) {
Packit Service a1bd4f
            g_hash_table_iter_init(&iter, priv->data_invalid);
Packit Service a1bd4f
            while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
Packit Service a1bd4f
                g_hash_table_insert(data, g_strdup(key), g_strdup(val));
Packit Service a1bd4f
        }
Packit Service a1bd4f
        g_value_take_boxed(value, data);
Packit Service a1bd4f
        break;
Packit Service a1bd4f
    default:
Packit Service a1bd4f
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
Packit Service a1bd4f
        break;
Packit Service a1bd4f
    }
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit Service a1bd4f
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = NM_SETTING_USER(object);
Packit Service a1bd4f
    NMSettingUserPrivate *priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit Service a1bd4f
    GHashTableIter        iter;
Packit Service a1bd4f
    GHashTable *          data;
Packit Service a1bd4f
    const char *          key, *val;
Packit Service a1bd4f
Packit Service a1bd4f
    switch (prop_id) {
Packit Service a1bd4f
    case PROP_DATA:
Packit Service a1bd4f
        nm_clear_g_free(&priv->keys);
Packit Service a1bd4f
Packit Service a1bd4f
        data = g_value_get_boxed(value);
Packit Service a1bd4f
        if (!data || !g_hash_table_size(data)) {
Packit Service a1bd4f
            nm_clear_pointer(&priv->data, g_hash_table_unref);
Packit Service a1bd4f
            nm_clear_pointer(&priv->data_invalid, g_hash_table_unref);
Packit Service a1bd4f
            return;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (priv->data)
Packit Service a1bd4f
            g_hash_table_remove_all(priv->data);
Packit Service a1bd4f
        else
Packit Service a1bd4f
            priv->data = _create_data_hash();
Packit Service a1bd4f
Packit Service a1bd4f
        if (priv->data_invalid)
Packit Service a1bd4f
            g_hash_table_remove_all(priv->data_invalid);
Packit Service a1bd4f
Packit Service a1bd4f
        g_hash_table_iter_init(&iter, data);
Packit Service a1bd4f
        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
Packit Service a1bd4f
            if (nm_setting_user_check_key(key, NULL) && nm_setting_user_check_val(val, NULL))
Packit Service a1bd4f
                g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
Packit Service a1bd4f
            else {
Packit Service a1bd4f
                if (!priv->data_invalid)
Packit Service a1bd4f
                    priv->data_invalid = _create_data_hash();
Packit Service a1bd4f
                g_hash_table_insert(priv->data_invalid, g_strdup(key), g_strdup(val));
Packit Service a1bd4f
            }
Packit Service a1bd4f
        }
Packit Service a1bd4f
        if (priv->data_invalid && !g_hash_table_size(priv->data_invalid))
Packit Service a1bd4f
            nm_clear_pointer(&priv->data_invalid, g_hash_table_unref);
Packit Service a1bd4f
Packit Service a1bd4f
        break;
Packit Service a1bd4f
    default:
Packit Service a1bd4f
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
Packit Service a1bd4f
        break;
Packit Service a1bd4f
    }
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static void
Packit Service a1bd4f
nm_setting_user_init(NMSettingUser *self)
Packit Service a1bd4f
{}
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * nm_setting_user_new:
Packit 5756e2
 *
Packit 5756e2
 * Creates a new #NMSettingUser object with default values.
Packit 5756e2
 *
Packit 5756e2
 * Returns: the new empty #NMSettingUser object
Packit 5756e2
 **/
Packit Service a1bd4f
NMSetting *
Packit Service a1bd4f
nm_setting_user_new(void)
Packit 5756e2
{
Packit Service a1bd4f
    return g_object_new(NM_TYPE_SETTING_USER, NULL);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit Service a1bd4f
finalize(GObject *object)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingUser *       self = NM_SETTING_USER(object);
Packit Service a1bd4f
    NMSettingUserPrivate *priv = NM_SETTING_USER_GET_PRIVATE(self);
Packit 5756e2
Packit Service a1bd4f
    g_free(priv->keys);
Packit Service a1bd4f
    if (priv->data)
Packit Service a1bd4f
        g_hash_table_unref(priv->data);
Packit Service a1bd4f
    if (priv->data_invalid)
Packit Service a1bd4f
        g_hash_table_unref(priv->data_invalid);
Packit 5756e2
Packit Service a1bd4f
    G_OBJECT_CLASS(nm_setting_user_parent_class)->finalize(object);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit Service a1bd4f
nm_setting_user_class_init(NMSettingUserClass *klass)
Packit 5756e2
{
Packit Service a1bd4f
    GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
Packit Service a1bd4f
    NMSettingClass *setting_class       = NM_SETTING_CLASS(klass);
Packit Service a1bd4f
    GArray *        properties_override = _nm_sett_info_property_override_create_array();
Packit Service a1bd4f
Packit Service a1bd4f
    object_class->get_property = get_property;
Packit Service a1bd4f
    object_class->set_property = set_property;
Packit Service a1bd4f
    object_class->finalize     = finalize;
Packit Service a1bd4f
Packit Service a1bd4f
    setting_class->compare_property = compare_property;
Packit Service a1bd4f
    setting_class->verify           = verify;
Packit Service a1bd4f
Packit Service a1bd4f
    /**
Packit Service a1bd4f
     * NMSettingUser:data: (type GHashTable(utf8,utf8))
Packit Service a1bd4f
     *
Packit Service a1bd4f
     * A dictionary of key/value pairs with user data. This data is ignored by NetworkManager
Packit Service a1bd4f
     * and can be used at the users discretion. The keys only support a strict ascii format,
Packit Service a1bd4f
     * but the values can be arbitrary UTF8 strings up to a certain length.
Packit Service a1bd4f
     *
Packit Service a1bd4f
     * Since: 1.8
Packit Service a1bd4f
     **/
Packit Service a1bd4f
    /* ---ifcfg-rh---
Packit Service a1bd4f
     * property: data
Packit Service a1bd4f
     * variable: NM_USER_*
Packit Service a1bd4f
     * description: each key/value pair is stored as a separate variable with
Packit Service a1bd4f
     *   name composed by concatenating NM_USER_ with the encoded key. The key is
Packit Service a1bd4f
     *   encoded by substituting lowercase letters with uppercase and prepending
Packit Service a1bd4f
     *   uppercase letters with an underscore. A dot is encoded as a double
Packit Service a1bd4f
     *   underscore. Remaining characters are encoded as underscore followed by a
Packit Service a1bd4f
     *   3 digit octal representation of the character.
Packit Service a1bd4f
     * example: NM_USER_FOO__BAR=something
Packit Service a1bd4f
     * ---end---
Packit Service a1bd4f
     */
Packit Service a1bd4f
    obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_USER_DATA,
Packit Service a1bd4f
                                                   "",
Packit Service a1bd4f
                                                   "",
Packit Service a1bd4f
                                                   G_TYPE_HASH_TABLE,
Packit Service a1bd4f
                                                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
Packit Service a1bd4f
    _nm_properties_override_gobj(properties_override,
Packit Service a1bd4f
                                 obj_properties[PROP_DATA],
Packit Service a1bd4f
                                 &nm_sett_info_propert_type_strdict);
Packit Service a1bd4f
Packit Service a1bd4f
    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
Packit Service a1bd4f
Packit Service a1bd4f
    _nm_setting_class_commit_full(setting_class,
Packit Service a1bd4f
                                  NM_META_SETTING_TYPE_USER,
Packit Service a1bd4f
                                  NULL,
Packit Service a1bd4f
                                  properties_override);
Packit 5756e2
}