Skip to content

Commit

Permalink
apr_escape: Add apr_escape_json() and apr_pescape_json().
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1914814 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
minfrin committed Dec 20, 2023
1 parent 2adcf63 commit 3646493
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes for APR 2.0.0

*) apr_escape: Add apr_escape_json() and apr_pescape_json().
[Graham Leggett]

*) dbm: Add LMDB driver. [Lubos Uhliarik <luhliari redhat.com>]

*) apr_memcache: Check sockets from connection pool before using them and try
Expand Down
267 changes: 266 additions & 1 deletion encoding/apr_escape.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* char in here and get it to work, because if char is signed then it
* will first be sign extended.
*/
#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
#define TEST_CHAR(c, f) (test_char_table[(apr_uint16_t)(c)] & (f))

APR_DECLARE(apr_status_t) apr_escape_shell(char *escaped, const char *str,
apr_ssize_t slen, apr_size_t *len)
Expand Down Expand Up @@ -1212,3 +1212,268 @@ APR_DECLARE(const char *) apr_pescape_ldap(apr_pool_t *p, const void *src,
return src;
}

APR_DECLARE(apr_status_t) apr_escape_json(char *escaped, const char *str,
apr_ssize_t slen, int quote, apr_size_t *len)
{
apr_size_t size = 1;
int found = quote;
int error = 0;
const unsigned char *s = (const unsigned char *) str;
unsigned char *d = (unsigned char *) escaped;
unsigned c;
const char invalid[3] = { 0xEF, 0xBF, 0xBD };

if (s) {
if (d) {
if (quote) {
*d++ = '"';
size += 1;
}
while ((c = *s) && slen) {
if (TEST_CHAR(c, T_ESCAPE_JSON)) {
switch (c) {
case '\b':
*d++ = '\\';
*d++ = 'b';
size += 2;
found = 1;
break;
case '\f':
*d++ = '\\';
*d++ = 'f';
size += 2;
found = 1;
break;
case '\n':
*d++ = '\\';
*d++ = 'n';
size += 2;
found = 1;
break;
case '\r':
*d++ = '\\';
*d++ = 'r';
size += 2;
found = 1;
break;
case '\t':
*d++ = '\\';
*d++ = 't';
size += 2;
found = 1;
break;
case '\\':
*d++ = '\\';
*d++ = '\\';
size += 2;
found = 1;
break;
case '"':
*d++ = '\\';
*d++ = '"';
size += 2;
found = 1;
break;
default:
if (c < 0x20) {
size += apr_snprintf((char *)d, 6, "\\u%04x", c);
d += 5;
found = 1;
}
else if (((c >> 7) == 0x00)) {
/* 1 byte */
memcpy(d, s, 1);
d += 1;
size += 1;
}
else if ((slen < 0 || slen > 1) && ((c >> 5) == 0x06) && s[1]) {
/* 2 bytes */
if ((slen > 0 && slen < 2) || (s[1] >> 6) != 0x02) {
memcpy(d, invalid, sizeof(invalid));
d += sizeof(invalid);
size += sizeof(invalid);
found = error = 1;
}
else {
memcpy(d, s, 2);
d += 2;
size += 2;
s += 1;
slen -= 1;
}
}
else if (((c >> 4) == 0x0E)) {
/* 3 bytes */
if ((slen > 0 && slen < 3) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02) {
memcpy(d, invalid, sizeof(invalid));
size += sizeof(invalid);
d += sizeof(invalid);
found = error = 1;
}
else {
memcpy(d, s, 3);
d += 3;
size += 3;
s += 2;
slen -= 2;
}
}
else if ((c >> 3) == 0x1E) {
/* 4 bytes */
if ((slen > 0 && slen < 4) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02 || (s[3] >> 6) != 0x02) {
memcpy(d, invalid, sizeof(invalid));
d += sizeof(invalid);
size += sizeof(invalid);
found = error = 1;
}
else {
memcpy(d, s, 4);
d += 4;
size += 4;
s += 3;
slen -= 3;
}
}
else {
memcpy(d, invalid, sizeof(invalid));
d += sizeof(invalid);
size += sizeof(invalid);
found = error = 1;
}
break;
}
}
else {
*d++ = c;
size++;
}
++s;
slen--;
}
if (quote) {
*d++ = '"';
size += 1;
}
*d = '\0';
}
else {
if (quote) {
size += 1;
}
while ((c = *s) && slen) {
if (TEST_CHAR(c, T_ESCAPE_JSON)) {
switch (c) {
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\\':
case '"':
size += 2;
found = 1;
break;
default:
if (c < 0x20) {
size += 5;
found = 1;
}
else if (((c >> 7) == 0x00)) {
/* 1 byte */
size += 1;
}
else if ((slen < 0 || slen > 1) && ((c >> 5) == 0x06) && s[1]) {
/* 2 bytes */
if ((slen > 0 && slen < 2) || (s[1] >> 6) != 0x02) {
size += sizeof(invalid);
found = error = 1;
}
else {
size += 2;
s += 1;
slen -= 1;
}
}
else if (((c >> 4) == 0x0E)) {
/* 3 bytes */
if ((slen > 0 && slen < 3) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02) {
size += sizeof(invalid);
found = error = 1;
}
else {
size += 3;
s += 2;
slen -= 2;
}
}
else if ((c >> 3) == 0x1E) {
/* 4 bytes */
if ((slen > 0 && slen < 4) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02 || (s[3] >> 6) != 0x02) {
size += sizeof(invalid);
found = error = 1;
}
else {
size += 4;
s += 3;
slen -= 3;
}
}
else {
size += sizeof(invalid);
found = error = 1;
}
}
}
else {
size++;
}
++s;
slen--;
}
if (quote) {
size += 1;
}
}
}

else if (quote) {
if (d) {
memcpy(d, "NULL", 5);
}
else {
size += 4;
}
}

if (len) {
*len = size;
}
if (error) {
return APR_EINVAL;
}
if (!found) {
return APR_NOTFOUND;
}

return APR_SUCCESS;
}

APR_DECLARE(const char *) apr_pescape_json(apr_pool_t *p, const char *src,
apr_ssize_t srclen, int quote)
{
apr_size_t len;

switch (apr_escape_json(NULL, src, srclen, quote, &len)) {
case APR_NOTFOUND: {
break;
}
default: {
char *encoded = apr_palloc(p, len);
apr_escape_json(encoded, src, srclen, quote, NULL);
return encoded;
}
}

return src;
}

32 changes: 32 additions & 0 deletions include/apr_escape.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,38 @@ APR_DECLARE(apr_status_t) apr_escape_ldap(char *dest, const void *src,
APR_DECLARE(const char *) apr_pescape_ldap(apr_pool_t *p, const void *src,
apr_ssize_t slen, int flags) __attribute__((nonnull(1)));

/**
* Apply JSON escaping to a UTF string. Invalid UTF8 character sequences
* are replaced by the U+FFFD replacement character.
* @param dest The destination buffer, can be NULL
* @param src The original buffer
* @param srclen The length of the original buffer. Pass APR_ESCAPE_STRING
* for a NUL terminated string.
* @param quote If non zero, surround the string with quotes, and if the
* string is NULL, return the string "NULL".
* @param len If present, returns the length of the string
* @return APR_SUCCESS, or APR_NOTFOUND if the string resulted in no
* modification, APR_EINVAL if bad UTF8 is detected. In all cases valid
* UTF8 is returned.
*/
APR_DECLARE(apr_status_t) apr_escape_json(char *dest, const char *src,
apr_ssize_t srclen, int quote, apr_size_t *len);

/**
* Apply JSON escaping to a UTF string. Invalid UTF8 character sequences
* are replaced by the U+FFFD replacement character.
* @param p Pool to allocate from
* @param src The original buffer
* @param srclen The length of the original buffer. Pass APR_ESCAPE_STRING
* for a NUL terminated string.
* @param quote If non zero, surround the string with quotes, and if the
* string is NULL, return the string "NULL".
* @return A zero padded buffer allocated from the pool on success, or
* NULL if src was NULL.
*/
APR_DECLARE(const char *) apr_pescape_json(apr_pool_t *p, const char *src,
apr_ssize_t srclen, int quote);

/** @} */
#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit 3646493

Please sign in to comment.