aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2022-08-15 17:52:59 +0200
committerDaniel Kiper <daniel.kiper@oracle.com>2022-08-19 23:55:50 +0200
commitb4055ebb8b3c1bbbc068f28099bd04b73df6fb67 (patch)
treece092deda3d78d7413d0191957370f28ac995231
parent5730424cb91b8215c8aa10ae35681dc7273522d9 (diff)
downloadgrub-b4055ebb8b3c1bbbc068f28099bd04b73df6fb67.tar.gz
lib/json/json: Add function to unescape JSON-encoded strings
JSON strings require certain characters to be encoded, either by using a single reverse solidus character "\" for a set of popular characters, or by using a Unicode representation of "\uXXXXX". The jsmn library doesn't handle unescaping for us, so we must implement this functionality for ourselves. Add a new function grub_json_unescape() that takes a potentially escaped JSON string as input and returns a new unescaped string. Signed-off-by: Patrick Steinhardt <ps@pks.im> Reviewed-by: Daniel Kiper <dkiper@net-space.pl>
-rw-r--r--grub-core/lib/json/json.c118
-rw-r--r--grub-core/lib/json/json.h12
2 files changed, 130 insertions, 0 deletions
diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c
index 1c20c75ea..1eadd1ce9 100644
--- a/grub-core/lib/json/json.c
+++ b/grub-core/lib/json/json.c
@@ -262,3 +262,121 @@ grub_json_getint64 (grub_int64_t *out, const grub_json_t *parent, const char *ke
return GRUB_ERR_NONE;
}
+
+grub_err_t
+grub_json_unescape (char **out, grub_size_t *outlen, const char *in, grub_size_t inlen)
+{
+ grub_err_t ret = GRUB_ERR_NONE;
+ grub_size_t inpos, resultpos;
+ char *result;
+
+ if (out == NULL || outlen == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("output parameters are not set"));
+
+ result = grub_calloc (1, inlen + 1);
+ if (result == NULL)
+ return GRUB_ERR_OUT_OF_MEMORY;
+
+ for (inpos = resultpos = 0; inpos < inlen; inpos++)
+ {
+ if (in[inpos] == '\\')
+ {
+ inpos++;
+ if (inpos >= inlen)
+ {
+ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("expected escaped character"));
+ goto err;
+ }
+
+ switch (in[inpos])
+ {
+ case '"':
+ result[resultpos++] = '"';
+ break;
+
+ case '/':
+ result[resultpos++] = '/';
+ break;
+
+ case '\\':
+ result[resultpos++] = '\\';
+ break;
+
+ case 'b':
+ result[resultpos++] = '\b';
+ break;
+
+ case 'f':
+ result[resultpos++] = '\f';
+ break;
+
+ case 'r':
+ result[resultpos++] = '\r';
+ break;
+
+ case 'n':
+ result[resultpos++] = '\n';
+ break;
+
+ case 't':
+ result[resultpos++] = '\t';
+ break;
+
+ case 'u':
+ {
+ char values[4] = {0};
+ unsigned i;
+
+ inpos++;
+ if (inpos + ARRAY_SIZE(values) > inlen)
+ {
+ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unicode sequence too short"));
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(values); i++)
+ {
+ char c = in[inpos++];
+
+ if (c >= '0' && c <= '9')
+ values[i] = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ values[i] = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ values[i] = c - 'a' + 10;
+ else
+ {
+ ret = grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unicode sequence with invalid character '%c'"), c);
+ goto err;
+ }
+ }
+
+ if (values[0] != 0 || values[1] != 0)
+ result[resultpos++] = values[0] << 4 | values[1];
+ result[resultpos++] = values[2] << 4 | values[3];
+
+ /* Offset the increment that's coming in via the loop increment. */
+ inpos--;
+
+ break;
+ }
+
+ default:
+ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized escaped character '%c'"), in[inpos]);
+ goto err;
+ }
+ }
+ else
+ result[resultpos++] = in[inpos];
+ }
+
+ *out = result;
+ *outlen = resultpos;
+
+ err:
+ if (ret != GRUB_ERR_NONE)
+ grub_free (result);
+
+ return ret;
+}
diff --git a/grub-core/lib/json/json.h b/grub-core/lib/json/json.h
index 4ea2a22d8..626074c35 100644
--- a/grub-core/lib/json/json.h
+++ b/grub-core/lib/json/json.h
@@ -125,4 +125,16 @@ extern grub_err_t EXPORT_FUNC(grub_json_getint64) (grub_int64_t *out,
const grub_json_t *parent,
const char *key);
+/*
+ * Unescape escaped characters and Unicode sequences in the
+ * given JSON-encoded string. Returns a newly allocated string
+ * passed back via the `out` parameter that has a length of
+ * `*outlen`.
+ *
+ * See https://datatracker.ietf.org/doc/html/rfc8259#section-7 for more
+ * information on escaping in JSON.
+ */
+extern grub_err_t EXPORT_FUNC(grub_json_unescape) (char **out, grub_size_t *outlen,
+ const char *in, grub_size_t inlen);
+
#endif