mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-28 08:40:09 +01:00
684 lines
21 KiB
C
684 lines
21 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/*
|
|
* Copyright (C) 2010 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-keyfile-utils.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "nm-glib-aux/nm-str-buf.h"
|
|
|
|
#include "nm-keyfile.h"
|
|
#include "nm-setting-wired.h"
|
|
#include "nm-setting-wireless.h"
|
|
#include "nm-setting-wireless-security.h"
|
|
|
|
#include "nm-keyfile-internal.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_key_file_get_boolean:
|
|
* @kf: the #GKeyFile
|
|
* @group: the group
|
|
* @key: the key
|
|
* @default_value: the default value if the value is set or not parsable as a boolean.
|
|
*
|
|
* Replacement for g_key_file_get_boolean() (which uses g_key_file_parse_value_as_boolean()).
|
|
* g_key_file_get_boolean() seems odd to me, because it accepts trailing ASCII whitespace,
|
|
* but not leading.
|
|
* This uses _nm_utils_ascii_str_to_bool(), which accepts trailing and leading whitespace,
|
|
* case-insensitive words, and also strings like "on" and "off".
|
|
* _nm_utils_ascii_str_to_bool() is our way to parse booleans from string, and we should
|
|
* use that one consistently.
|
|
*
|
|
* Also, it doesn't have g_key_file_get_boolean()'s odd API to require an error argument
|
|
* to detect parsing failures.
|
|
*
|
|
* Returns: either %TRUE or %FALSE if the key exists and is parsable as a boolean.
|
|
* Otherwise, @default_value. Sets errno to ENODATA, EINVAL or 0, depending on whether
|
|
* the key exists, whether the value is invalid, or success.
|
|
*/
|
|
int
|
|
nm_key_file_get_boolean(GKeyFile *kf, const char *group, const char *key, int default_value)
|
|
{
|
|
int v;
|
|
gs_free char *value = NULL;
|
|
|
|
value = g_key_file_get_value(kf, group, key, NULL);
|
|
|
|
if (!value) {
|
|
errno = ENODATA;
|
|
return default_value;
|
|
}
|
|
v = _nm_utils_ascii_str_to_bool(value, -1);
|
|
if (v != -1) {
|
|
errno = 0;
|
|
return v;
|
|
}
|
|
errno = EINVAL;
|
|
return default_value;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
const char *setting;
|
|
const char *alias;
|
|
} SettingAlias;
|
|
|
|
static const SettingAlias alias_list[] = {
|
|
{NM_SETTING_WIRED_SETTING_NAME, "ethernet"},
|
|
{NM_SETTING_WIRELESS_SETTING_NAME, "wifi"},
|
|
{NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security"},
|
|
};
|
|
|
|
const char *
|
|
nm_keyfile_plugin_get_alias_for_setting_name(const char *setting_name)
|
|
{
|
|
guint i;
|
|
|
|
g_return_val_if_fail(setting_name != NULL, NULL);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(alias_list); i++) {
|
|
if (nm_streq(setting_name, alias_list[i].setting))
|
|
return alias_list[i].alias;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
nm_keyfile_plugin_get_setting_name_for_alias(const char *alias)
|
|
{
|
|
guint i;
|
|
|
|
g_return_val_if_fail(alias != NULL, NULL);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(alias_list); i++) {
|
|
if (nm_streq(alias, alias_list[i].alias))
|
|
return alias_list[i].setting;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
char **
|
|
nm_keyfile_plugin_kf_get_string_list(GKeyFile * kf,
|
|
const char *group,
|
|
const char *key,
|
|
gsize * out_length,
|
|
GError ** error)
|
|
{
|
|
char ** list;
|
|
const char *alias;
|
|
GError * local = NULL;
|
|
gsize l;
|
|
|
|
list = g_key_file_get_string_list(kf, group, key, &l, &local);
|
|
if (nm_g_error_matches(local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group);
|
|
if (alias) {
|
|
g_clear_error(&local);
|
|
list = g_key_file_get_string_list(kf, alias, key, &l, &local);
|
|
}
|
|
}
|
|
if (local)
|
|
g_propagate_error(error, local);
|
|
if (!list)
|
|
l = 0;
|
|
NM_SET_OUT(out_length, l);
|
|
return list;
|
|
}
|
|
|
|
guint *
|
|
nm_keyfile_plugin_kf_get_integer_list_uint(GKeyFile * key_file,
|
|
const char *group_name,
|
|
const char *key,
|
|
gsize * out_length,
|
|
GError ** error)
|
|
{
|
|
GError * key_file_error = NULL;
|
|
gs_strfreev char **values = NULL;
|
|
gs_free guint *int_values = NULL;
|
|
gsize i, num_ints;
|
|
|
|
NM_SET_OUT(out_length, 0);
|
|
|
|
g_return_val_if_fail(key_file != NULL, NULL);
|
|
g_return_val_if_fail(group_name != NULL, NULL);
|
|
g_return_val_if_fail(key != NULL, NULL);
|
|
|
|
values =
|
|
nm_keyfile_plugin_kf_get_string_list(key_file, group_name, key, &num_ints, &key_file_error);
|
|
|
|
if (key_file_error)
|
|
g_propagate_error(error, key_file_error);
|
|
if (!values)
|
|
return NULL;
|
|
|
|
int_values = g_new(guint, num_ints);
|
|
|
|
for (i = 0; i < num_ints; i++) {
|
|
gint64 v;
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(v) > sizeof(guint));
|
|
v = _nm_utils_ascii_str_to_int64(values[i], 10, 0, G_MAXUINT, -1);
|
|
if (v == -1) {
|
|
g_set_error(error,
|
|
G_KEY_FILE_ERROR,
|
|
G_KEY_FILE_ERROR_INVALID_VALUE,
|
|
_("Value cannot be interpreted as a list of numbers."));
|
|
return NULL;
|
|
}
|
|
|
|
int_values[i] = v;
|
|
}
|
|
|
|
NM_SET_OUT(out_length, num_ints);
|
|
return g_steal_pointer(&int_values);
|
|
}
|
|
|
|
void
|
|
nm_keyfile_plugin_kf_set_string_list(GKeyFile * kf,
|
|
const char * group,
|
|
const char * key,
|
|
const char *const *list,
|
|
gsize length)
|
|
{
|
|
const char *alias;
|
|
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group);
|
|
g_key_file_set_string_list(kf, alias ?: group, key, list, length);
|
|
}
|
|
|
|
void
|
|
nm_keyfile_plugin_kf_set_integer_list_uint(GKeyFile * kf,
|
|
const char * group,
|
|
const char * key,
|
|
const guint *data,
|
|
gsize length)
|
|
{
|
|
nm_auto_str_buf NMStrBuf strbuf = {};
|
|
gsize i;
|
|
|
|
g_return_if_fail(kf);
|
|
g_return_if_fail(!length || data);
|
|
g_return_if_fail(group && group[0]);
|
|
g_return_if_fail(key && key[0]);
|
|
|
|
nm_str_buf_init(&strbuf, length * 4u + 2u, FALSE);
|
|
for (i = 0; i < length; i++)
|
|
nm_str_buf_append_printf(&strbuf, "%u;", data[i]);
|
|
nm_keyfile_plugin_kf_set_value(kf, group, key, nm_str_buf_get_str(&strbuf));
|
|
}
|
|
|
|
void
|
|
nm_keyfile_plugin_kf_set_integer_list_uint8(GKeyFile * kf,
|
|
const char * group,
|
|
const char * key,
|
|
const guint8 *data,
|
|
gsize length)
|
|
{
|
|
nm_auto_str_buf NMStrBuf strbuf = {};
|
|
gsize i;
|
|
|
|
g_return_if_fail(kf);
|
|
g_return_if_fail(!length || data);
|
|
g_return_if_fail(group && group[0]);
|
|
g_return_if_fail(key && key[0]);
|
|
|
|
nm_str_buf_init(&strbuf, length * 4u + 2u, FALSE);
|
|
for (i = 0; i < length; i++)
|
|
nm_str_buf_append_printf(&strbuf, "%u;", (guint) data[i]);
|
|
nm_keyfile_plugin_kf_set_value(kf, group, key, nm_str_buf_get_str(&strbuf));
|
|
}
|
|
|
|
#define DEFINE_KF_WRAPPER_GET(fcn_name, get_ctype, key_file_get_fcn) \
|
|
get_ctype fcn_name(GKeyFile *kf, const char *group, const char *key, GError **error) \
|
|
{ \
|
|
get_ctype val; \
|
|
const char *alias; \
|
|
GError * local = NULL; \
|
|
\
|
|
val = key_file_get_fcn(kf, group, key, &local); \
|
|
if (nm_g_error_matches(local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group); \
|
|
if (alias) { \
|
|
g_clear_error(&local); \
|
|
val = key_file_get_fcn(kf, alias, key, &local); \
|
|
} \
|
|
} \
|
|
if (local) \
|
|
g_propagate_error(error, local); \
|
|
return val; \
|
|
}
|
|
|
|
DEFINE_KF_WRAPPER_GET(nm_keyfile_plugin_kf_get_string, char *, g_key_file_get_string);
|
|
DEFINE_KF_WRAPPER_GET(nm_keyfile_plugin_kf_get_boolean, gboolean, g_key_file_get_boolean);
|
|
DEFINE_KF_WRAPPER_GET(nm_keyfile_plugin_kf_get_value, char *, g_key_file_get_value);
|
|
|
|
#define DEFINE_KF_WRAPPER_SET(fcn_name, set_ctype, key_file_set_fcn) \
|
|
void fcn_name(GKeyFile *kf, const char *group, const char *key, set_ctype value) \
|
|
{ \
|
|
const char *alias; \
|
|
\
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group); \
|
|
key_file_set_fcn(kf, alias ?: group, key, value); \
|
|
}
|
|
|
|
DEFINE_KF_WRAPPER_SET(nm_keyfile_plugin_kf_set_string, const char *, g_key_file_set_string);
|
|
DEFINE_KF_WRAPPER_SET(nm_keyfile_plugin_kf_set_boolean, gboolean, g_key_file_set_boolean);
|
|
DEFINE_KF_WRAPPER_SET(nm_keyfile_plugin_kf_set_value, const char *, g_key_file_set_value);
|
|
|
|
gint64
|
|
nm_keyfile_plugin_kf_get_int64(GKeyFile * kf,
|
|
const char *group,
|
|
const char *key,
|
|
guint base,
|
|
gint64 min,
|
|
gint64 max,
|
|
gint64 fallback,
|
|
GError ** error)
|
|
{
|
|
gs_free char *s = NULL;
|
|
int errsv;
|
|
gint64 v;
|
|
|
|
s = nm_keyfile_plugin_kf_get_value(kf, group, key, error);
|
|
if (!s) {
|
|
errno = ENODATA;
|
|
return fallback;
|
|
}
|
|
|
|
v = _nm_utils_ascii_str_to_int64(s, base, min, max, fallback);
|
|
errsv = errno;
|
|
if (errsv != 0 && error) {
|
|
g_set_error(error,
|
|
G_KEY_FILE_ERROR,
|
|
G_KEY_FILE_ERROR_INVALID_VALUE,
|
|
_("value is not an integer in range [%lld, %lld]"),
|
|
(long long) min,
|
|
(long long) max);
|
|
errno = errsv;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
char **
|
|
nm_keyfile_plugin_kf_get_keys(GKeyFile *kf, const char *group, gsize *out_length, GError **error)
|
|
{
|
|
char ** keys;
|
|
const char *alias;
|
|
GError * local = NULL;
|
|
gsize l;
|
|
|
|
keys = g_key_file_get_keys(kf, group, &l, &local);
|
|
if (nm_g_error_matches(local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group);
|
|
if (alias) {
|
|
g_clear_error(&local);
|
|
keys = g_key_file_get_keys(kf, alias, &l, error ? &local : NULL);
|
|
}
|
|
}
|
|
nm_assert((!local) != (!keys));
|
|
if (!keys)
|
|
l = 0;
|
|
nm_assert(l == NM_PTRARRAY_LEN(keys));
|
|
NM_SET_OUT(out_length, l);
|
|
if (local)
|
|
g_propagate_error(error, local);
|
|
return keys;
|
|
}
|
|
|
|
gboolean
|
|
nm_keyfile_plugin_kf_has_key(GKeyFile *kf, const char *group, const char *key, GError **error)
|
|
{
|
|
gboolean has;
|
|
const char *alias;
|
|
GError * local = NULL;
|
|
|
|
has = g_key_file_has_key(kf, group, key, &local);
|
|
if (nm_g_error_matches(local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
|
|
alias = nm_keyfile_plugin_get_alias_for_setting_name(group);
|
|
if (alias) {
|
|
g_clear_error(&local);
|
|
has = g_key_file_has_key(kf, alias, key, &local);
|
|
}
|
|
}
|
|
if (local)
|
|
g_propagate_error(error, local);
|
|
return has;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
_nm_keyfile_copy(GKeyFile *dst, GKeyFile *src)
|
|
{
|
|
gs_strfreev char **groups = NULL;
|
|
guint g, k;
|
|
|
|
groups = g_key_file_get_groups(src, NULL);
|
|
for (g = 0; groups && groups[g]; g++) {
|
|
const char * group = groups[g];
|
|
gs_strfreev char **keys = NULL;
|
|
|
|
keys = g_key_file_get_keys(src, group, NULL, NULL);
|
|
if (!keys)
|
|
continue;
|
|
|
|
for (k = 0; keys[k]; k++) {
|
|
const char * key = keys[k];
|
|
gs_free char *value = NULL;
|
|
|
|
value = g_key_file_get_value(src, group, key, NULL);
|
|
if (value)
|
|
g_key_file_set_value(dst, group, key, value);
|
|
else
|
|
g_key_file_remove_key(dst, group, key, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
_nm_keyfile_a_contains_all_in_b(GKeyFile *kf_a, GKeyFile *kf_b)
|
|
{
|
|
gs_strfreev char **groups = NULL;
|
|
guint i, j;
|
|
|
|
if (kf_a == kf_b)
|
|
return TRUE;
|
|
if (!kf_a || !kf_b)
|
|
return FALSE;
|
|
|
|
groups = g_key_file_get_groups(kf_a, NULL);
|
|
for (i = 0; groups && groups[i]; i++) {
|
|
gs_strfreev char **keys = NULL;
|
|
|
|
keys = g_key_file_get_keys(kf_a, groups[i], NULL, NULL);
|
|
if (!keys)
|
|
continue;
|
|
|
|
for (j = 0; keys[j]; j++) {
|
|
gs_free char *key_a = g_key_file_get_value(kf_a, groups[i], keys[j], NULL);
|
|
gs_free char *key_b = g_key_file_get_value(kf_b, groups[i], keys[j], NULL);
|
|
|
|
if (g_strcmp0(key_a, key_b) != 0)
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_nm_keyfile_equals_ordered(GKeyFile *kf_a, GKeyFile *kf_b)
|
|
{
|
|
gs_strfreev char **groups = NULL;
|
|
gs_strfreev char **groups_b = NULL;
|
|
guint i, j;
|
|
|
|
if (kf_a == kf_b)
|
|
return TRUE;
|
|
if (!kf_a || !kf_b)
|
|
return FALSE;
|
|
|
|
groups = g_key_file_get_groups(kf_a, NULL);
|
|
groups_b = g_key_file_get_groups(kf_b, NULL);
|
|
if (!groups && !groups_b)
|
|
return TRUE;
|
|
if (!groups || !groups_b)
|
|
return FALSE;
|
|
for (i = 0; groups[i] && groups_b[i] && !strcmp(groups[i], groups_b[i]); i++)
|
|
;
|
|
if (groups[i] || groups_b[i])
|
|
return FALSE;
|
|
|
|
for (i = 0; groups[i]; i++) {
|
|
gs_strfreev char **keys = NULL;
|
|
gs_strfreev char **keys_b = NULL;
|
|
|
|
keys = g_key_file_get_keys(kf_a, groups[i], NULL, NULL);
|
|
keys_b = g_key_file_get_keys(kf_b, groups[i], NULL, NULL);
|
|
|
|
if ((!keys) != (!keys_b))
|
|
return FALSE;
|
|
if (!keys)
|
|
continue;
|
|
|
|
for (j = 0; keys[j] && keys_b[j] && !strcmp(keys[j], keys_b[j]); j++)
|
|
;
|
|
if (keys[j] || keys_b[j])
|
|
return FALSE;
|
|
|
|
for (j = 0; keys[j]; j++) {
|
|
gs_free char *key_a = g_key_file_get_value(kf_a, groups[i], keys[j], NULL);
|
|
gs_free char *key_b = g_key_file_get_value(kf_b, groups[i], keys[j], NULL);
|
|
|
|
if (g_strcmp0(key_a, key_b) != 0)
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_nm_keyfile_equals(GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order)
|
|
{
|
|
if (!consider_order) {
|
|
return _nm_keyfile_a_contains_all_in_b(kf_a, kf_b)
|
|
&& _nm_keyfile_a_contains_all_in_b(kf_b, kf_a);
|
|
} else {
|
|
return _nm_keyfile_equals_ordered(kf_a, kf_b);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
_nm_keyfile_has_values(GKeyFile *keyfile)
|
|
{
|
|
gs_strfreev char **groups = NULL;
|
|
|
|
g_return_val_if_fail(keyfile, FALSE);
|
|
|
|
groups = g_key_file_get_groups(keyfile, NULL);
|
|
return groups && groups[0];
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const char *
|
|
_keyfile_key_encode(const char *name, char **out_to_free)
|
|
{
|
|
NMStrBuf str;
|
|
gsize len;
|
|
gsize i;
|
|
|
|
nm_assert(name);
|
|
nm_assert(out_to_free && !*out_to_free);
|
|
|
|
/* See g_key_file_is_key_name().
|
|
*
|
|
* GKeyFile allows all UTF-8 characters (even non-well formed sequences),
|
|
* except:
|
|
* - no empty keys
|
|
* - no leading/trailing ' '
|
|
* - no '=', '[', ']'
|
|
*
|
|
* We do something more strict here. All non-ASCII characters, all non-printable
|
|
* characters, and all invalid characters are escaped with "\\XX".
|
|
*
|
|
* We don't escape \\, unless it is followed by two hex digits.
|
|
*/
|
|
|
|
if (!name[0]) {
|
|
/* empty keys are backslash encoded. Note that usually
|
|
* \\00 is not a valid encode, the only exception is the empty
|
|
* word. */
|
|
return "\\00";
|
|
}
|
|
|
|
/* find the first character that needs escaping. */
|
|
i = 0;
|
|
if (name[0] != ' ') {
|
|
for (;; i++) {
|
|
const guchar ch = (guchar) name[i];
|
|
|
|
if (ch == '\0')
|
|
return name;
|
|
|
|
if (ch < 0x20 || ch >= 127 || NM_IN_SET(ch, '=', '[', ']')
|
|
|| (ch == '\\' && g_ascii_isxdigit(name[i + 1]) && g_ascii_isxdigit(name[i + 2]))
|
|
|| (ch == ' ' && name[i + 1] == '\0'))
|
|
break;
|
|
}
|
|
} else if (name[1] == '\0')
|
|
return "\\20";
|
|
|
|
len = i + strlen(&name[i]);
|
|
nm_assert(len == strlen(name));
|
|
|
|
nm_str_buf_init(&str, len + 15u, FALSE);
|
|
|
|
if (name[0] == ' ') {
|
|
nm_assert(i == 0);
|
|
nm_str_buf_append(&str, "\\20");
|
|
i = 1;
|
|
} else
|
|
nm_str_buf_append_len(&str, name, i);
|
|
|
|
for (;; i++) {
|
|
const guchar ch = (guchar) name[i];
|
|
|
|
if (ch == '\0')
|
|
break;
|
|
|
|
if (ch < 0x20 || ch >= 127 || NM_IN_SET(ch, '=', '[', ']')
|
|
|| (ch == '\\' && g_ascii_isxdigit(name[i + 1]) && g_ascii_isxdigit(name[i + 2]))
|
|
|| (ch == ' ' && name[i + 1] == '\0')) {
|
|
nm_str_buf_append_c(&str, '\\');
|
|
nm_str_buf_append_c_hex(&str, ch, TRUE);
|
|
} else
|
|
nm_str_buf_append_c(&str, (char) ch);
|
|
}
|
|
|
|
return (*out_to_free = nm_str_buf_finalize(&str, NULL));
|
|
}
|
|
|
|
static const char *
|
|
_keyfile_key_decode(const char *key, char **out_to_free)
|
|
{
|
|
char *out;
|
|
gsize len;
|
|
gsize i;
|
|
gsize j;
|
|
|
|
nm_assert(key);
|
|
nm_assert(out_to_free && !*out_to_free);
|
|
|
|
if (!key[0])
|
|
return "";
|
|
|
|
for (i = 0; TRUE; i++) {
|
|
const char ch = key[i];
|
|
|
|
if (ch == '\0')
|
|
return key;
|
|
if (ch == '\\' && g_ascii_isxdigit(key[i + 1]) && g_ascii_isxdigit(key[i + 2]))
|
|
break;
|
|
}
|
|
|
|
len = i + strlen(&key[i]);
|
|
|
|
if (len == 3 && nm_streq(key, "\\00"))
|
|
return "";
|
|
|
|
nm_assert(len == strlen(key));
|
|
|
|
out = g_new(char, len + 1u);
|
|
|
|
memcpy(out, key, sizeof(char) * i);
|
|
|
|
j = i;
|
|
for (;;) {
|
|
const char ch = key[i];
|
|
char ch1, ch2;
|
|
unsigned v;
|
|
|
|
if (ch == '\0')
|
|
break;
|
|
|
|
if (ch == '\\' && g_ascii_isxdigit((ch1 = key[i + 1]))
|
|
&& g_ascii_isxdigit((ch2 = key[i + 2]))) {
|
|
v = (g_ascii_xdigit_value(ch1) << 4) + g_ascii_xdigit_value(ch2);
|
|
if (v != 0) {
|
|
out[j++] = (char) v;
|
|
i += 3;
|
|
continue;
|
|
}
|
|
}
|
|
out[j++] = ch;
|
|
i++;
|
|
}
|
|
|
|
nm_assert(j <= len);
|
|
out[j] = '\0';
|
|
return (*out_to_free = out);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nm_keyfile_key_encode(const char *name, char **out_to_free)
|
|
{
|
|
const char *key;
|
|
|
|
key = _keyfile_key_encode(name, out_to_free);
|
|
#if NM_MORE_ASSERTS > 5
|
|
nm_assert(key);
|
|
nm_assert(!*out_to_free || key == *out_to_free);
|
|
nm_assert(!*out_to_free || !nm_streq0(name, key));
|
|
{
|
|
gs_free char *to_free2 = NULL;
|
|
const char * name2;
|
|
|
|
name2 = _keyfile_key_decode(key, &to_free2);
|
|
/* name2, the result of encode()+decode() is identical to name.
|
|
* That is because
|
|
* - encode() is a injective function.
|
|
* - decode() is a surjective function, however for output
|
|
* values of encode() is behaves injective too. */
|
|
nm_assert(nm_streq0(name2, name));
|
|
}
|
|
#endif
|
|
return key;
|
|
}
|
|
|
|
const char *
|
|
nm_keyfile_key_decode(const char *key, char **out_to_free)
|
|
{
|
|
const char *name;
|
|
|
|
name = _keyfile_key_decode(key, out_to_free);
|
|
#if NM_MORE_ASSERTS > 5
|
|
nm_assert(name);
|
|
nm_assert(!*out_to_free || name == *out_to_free);
|
|
{
|
|
gs_free char *to_free2 = NULL;
|
|
const char * key2;
|
|
|
|
key2 = _keyfile_key_encode(name, &to_free2);
|
|
/* key2, the result of decode+encode may not be identical
|
|
* to the original key. That is, decode() is a surjective
|
|
* function mapping different keys to the same name.
|
|
* However, decode() behaves injective for input that
|
|
* are valid output of encode(). */
|
|
nm_assert(key2);
|
|
}
|
|
#endif
|
|
return name;
|
|
}
|