From 0f22f77b1c1f871b0ae85f292e68324413283d06 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 4 May 2020 09:52:16 +0200 Subject: [PATCH] shared: support stripping whitespace from nm_utils_buf_utf8safe_unescape() When parsing user input if is often convenient to allow stripping whitespace. Especially with escaped strings, the user could still escape the whitespace, if the space should be taken literally. Add support for that to nm_utils_buf_utf8safe_unescape(). Note that this is not the same as calling g_strstrip() before/after unescape. That is, because nm_utils_buf_utf8safe_unescape() correctly preserves escaped whitespace. If you call g_strstrip() before/after the unescape, you don't know whether the whitespace is escaped. --- libnm-core/nm-setting-ip-config.c | 2 +- libnm-core/nm-utils.c | 2 +- libnm-core/tests/test-general.c | 12 ++++----- libnm/nm-libnm-utils.c | 2 +- shared/nm-glib-aux/nm-shared-utils.c | 37 ++++++++++++++++++++++------ shared/nm-glib-aux/nm-shared-utils.h | 14 ++++++++--- src/nm-config.c | 2 +- 7 files changed, 50 insertions(+), 21 deletions(-) diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 36f09b694..09a342836 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -2186,7 +2186,7 @@ nm_ip_routing_rule_get_xifname_bin (const NMIPRoutingRule *self, if (!xifname) return FALSE; - bin = nm_utils_buf_utf8safe_unescape (xifname, &len, &bin_to_free); + bin = nm_utils_buf_utf8safe_unescape (xifname, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &len, &bin_to_free); strncpy (out_xifname, bin, 16 /* IFNAMSIZ */); out_xifname[15] = '\0'; diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 7b69b4ef7..f9bccea93 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4673,7 +4673,7 @@ nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name) g_return_val_if_fail (utf8safe_name, FALSE); - bin = nm_utils_buf_utf8safe_unescape (utf8safe_name, &len, &bin_to_free); + bin = nm_utils_buf_utf8safe_unescape (utf8safe_name, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &len, &bin_to_free); if (bin_to_free) { /* some unescaping happened... */ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 7da247964..52f05dc30 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -7815,7 +7815,7 @@ _do_test_utils_str_utf8safe_unescape (const char *str, const char *expected, gsi gs_free gpointer buf_free_1 = NULL; gs_free char *str_free_1 = NULL; - s = nm_utils_buf_utf8safe_unescape (str, &l, &buf_free_1); + s = nm_utils_buf_utf8safe_unescape (str, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &l, &buf_free_1); g_assert_cmpint (expected_len, ==, l); g_assert_cmpstr (s, ==, expected); @@ -7839,7 +7839,7 @@ _do_test_utils_str_utf8safe_unescape (const char *str, const char *expected, gsi if ( expected && l == strlen (expected)) { /* there are no embeeded NULs. Check that nm_utils_str_utf8safe_unescape() yields the same result. */ - s = nm_utils_str_utf8safe_unescape (str, &str_free_1); + s = nm_utils_str_utf8safe_unescape (str, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &str_free_1); g_assert_cmpstr (s, ==, expected); if (strchr (str, '\\')) { g_assert (str_free_1 != str); @@ -7908,10 +7908,10 @@ _do_test_utils_str_utf8safe (const char *str, gsize str_len, const char *expecte g_assert (g_utf8_validate (str, -1, NULL)); } - g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str_free_4)); + g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &str_free_4)); g_assert (!str_free_4); - str_free_5 = nm_utils_str_utf8safe_unescape_cp (str_safe); + str_free_5 = nm_utils_str_utf8safe_unescape_cp (str_safe, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); if (str) { g_assert (str_free_5 != str); g_assert_cmpstr (str_free_5, ==, str); @@ -7935,11 +7935,11 @@ _do_test_utils_str_utf8safe (const char *str, gsize str_len, const char *expecte str_free_6 = g_strcompress (str_safe); g_assert_cmpstr (str, ==, str_free_6); - str_free_7 = nm_utils_str_utf8safe_unescape_cp (str_safe); + str_free_7 = nm_utils_str_utf8safe_unescape_cp (str_safe, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); g_assert (str_free_7 != str); g_assert_cmpstr (str_free_7, ==, str); - s = nm_utils_str_utf8safe_unescape (str_safe, &str_free_8); + s = nm_utils_str_utf8safe_unescape (str_safe, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, &str_free_8); g_assert (str_free_8 != str); g_assert (s == str_free_8); g_assert_cmpstr (str_free_8, ==, str); diff --git a/libnm/nm-libnm-utils.c b/libnm/nm-libnm-utils.c index 7b4db11ef..f8d1ff560 100644 --- a/libnm/nm-libnm-utils.c +++ b/libnm/nm-libnm-utils.c @@ -139,7 +139,7 @@ _fixup_string (const char *desc, return NULL; /* restore original non-UTF-8-safe text. */ - desc_full = nm_utils_str_utf8safe_unescape_cp (desc); + desc_full = nm_utils_str_utf8safe_unescape_cp (desc, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); /* replace all invalid UTF-8 bytes with space. */ p = desc_full; diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 56729a3b9..96b09b16d 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -2456,11 +2456,12 @@ _str_buf_append_c_escape_octal (NMStrBuf *strbuf, } gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free) { + gboolean strip_spaces = NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); NMStrBuf strbuf; - gsize len; const char *s; + gsize len; g_return_val_if_fail (to_free, NULL); g_return_val_if_fail (out_len, NULL); @@ -2471,10 +2472,23 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr return NULL; } + if (strip_spaces) + str = nm_str_skip_leading_spaces (str); + len = strlen (str); s = memchr (str, '\\', len); if (!s) { + if ( strip_spaces + && len > 0 + && g_ascii_isspace (str[len - 1])) { + len--; + while ( len > 0 + && g_ascii_isspace (str[len - 1])) + len--; + *out_len = len; + return (*to_free = g_strndup (str, len)); + } *out_len = len; *to_free = NULL; return str; @@ -2494,7 +2508,7 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr ch = (++str)[0]; if (ch == '\0') { - // error. Trailing '\\' + /* error. Trailing '\\' */ break; } @@ -2533,7 +2547,14 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr s = strchr (str, '\\'); if (!s) { - nm_str_buf_append (&strbuf, str); + gsize l = strlen (str); + + if (strip_spaces) { + while ( l > 0 + && g_ascii_isspace (str[l - 1])) + l--; + } + nm_str_buf_append_len (&strbuf, str, l); break; } @@ -2678,14 +2699,14 @@ nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags /*****************************************************************************/ const char * -nm_utils_str_utf8safe_unescape (const char *str, char **to_free) +nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { const char *res; gsize len; g_return_val_if_fail (to_free, NULL); - res = nm_utils_buf_utf8safe_unescape (str, &len, (gpointer *) to_free); + res = nm_utils_buf_utf8safe_unescape (str, flags, &len, (gpointer *) to_free); nm_assert ( (!res && len == 0) || (strlen (res) <= len)); @@ -2748,11 +2769,11 @@ nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) } char * -nm_utils_str_utf8safe_unescape_cp (const char *str) +nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) { char *s; - str = nm_utils_str_utf8safe_unescape (str, &s); + str = nm_utils_str_utf8safe_unescape (str, flags, &s); return s ?: g_strdup (str); } diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index fde1499ab..a2530efaf 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1197,17 +1197,25 @@ typedef enum { * reallocation happens. Thus, unescape always avoids leaking * secrets already. */ NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0004, + + /* This flag only has an effect during unescaping. It means + * that non-escaped whitespaces (g_ascii_isspace()) will be + * stripped from the front and end of the string. Note that + * this flag is only useful for gracefully accepting user input + * with spaces. With this flag, escape and unescape may no longer + * yield the original input. */ + NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0008, } NMUtilsStrUtf8SafeFlags; const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free); -gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free); +gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free); const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); -const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free); +const char *nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); -char *nm_utils_str_utf8safe_unescape_cp (const char *str); +char *nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); diff --git a/src/nm-config.c b/src/nm-config.c index d63805958..4fa853194 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -344,7 +344,7 @@ no_auto_default_from_file (const char *no_auto_default_file) if (list) { for (i = 0; list[i]; i++) - list[i] = nm_utils_str_utf8safe_unescape_cp (list[i]); + list[i] = nm_utils_str_utf8safe_unescape_cp (list[i], NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); } /* The returned buffer here is not at all compact. That means, it has additional