/* Icecast * * This program is distributed under the GNU General Public License, version 2. * A copy of this license is included with this source. * * Copyright 2018-2020, Philipp "ph3-der-loewe" Schafft , */ /** * This file contains functions for rendering JSON. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "json.h" #include "logging.h" #define CATMODULE "json" #define MAX_RECURSION 64 struct json_renderer_tag { unsigned int flags; int valid; char *buffer; size_t bufferlen; size_t bufferfill; char levelinfo[MAX_RECURSION]; size_t level; }; static int allocate_buffer(json_renderer_t *renderer, size_t needed) { size_t required = needed + renderer->level; size_t have = renderer->bufferlen - renderer->bufferfill; if (!renderer->valid) return 1; if (have) have--; if (have < required) { size_t want; char *n; if (required < 128) required = 128; want = renderer->bufferfill + required; if (want < 512) want = 512; n = realloc(renderer->buffer, want); if (!n) return 1; renderer->buffer = n; renderer->bufferlen = want; } return 0; } static void json_renderer_destroy(json_renderer_t *renderer) { if (!renderer) return; renderer->valid = 0; free(renderer->buffer); free(renderer); } json_renderer_t * json_renderer_create(unsigned int flags) { json_renderer_t *renderer = calloc(1, sizeof(json_renderer_t)); if (!renderer) return NULL; renderer->flags = flags; renderer->valid = 1; renderer->levelinfo[0] = '0'; renderer->level = 1; if (allocate_buffer(renderer, 0) != 0) { json_renderer_destroy(renderer); return NULL; } return renderer; } char * json_renderer_finish(json_renderer_t **rendererptr) { json_renderer_t *renderer; char *ret; if (!rendererptr) return NULL; renderer = *rendererptr; *rendererptr = NULL; if (!renderer) return NULL; if (!renderer->valid) { json_renderer_destroy(renderer); return NULL; } for (; renderer->level; renderer->level--) { switch (renderer->levelinfo[renderer->level-1]) { case '0': renderer->buffer[renderer->bufferfill++] = 0; break; case 'o': case 'O': renderer->buffer[renderer->bufferfill++] = '}'; break; case 'a': case 'A': renderer->buffer[renderer->bufferfill++] = ']'; break; default: json_renderer_destroy(renderer); return NULL; break; } } ret = renderer->buffer; renderer->buffer = NULL; json_renderer_destroy(renderer); return ret; } static int write_raw(json_renderer_t *renderer, const char *raw, int begin) { size_t rawlen = strlen(raw); size_t want = rawlen; char level; char seperator = 0; if (!renderer->valid) return 1; level = renderer->levelinfo[renderer->level-1]; if (begin) { if (level == 'O' || level == 'A') { seperator = ','; } else if (level == 'o') { renderer->levelinfo[renderer->level-1] = 'O'; } else if (level == 'a') { renderer->levelinfo[renderer->level-1] = 'A'; } else if (level == 'P') { seperator = ':'; renderer->level--; } } if (seperator) want++; if (allocate_buffer(renderer, want) != 0) return 1; if (seperator) renderer->buffer[renderer->bufferfill++] = seperator; memcpy(&(renderer->buffer[renderer->bufferfill]), raw, rawlen); renderer->bufferfill += rawlen; return 0; } static int want_write_value(json_renderer_t *renderer) { char level; if (!renderer || !renderer->valid) return 1; level = renderer->levelinfo[renderer->level-1]; if (level == 'o' || level == 'O') { renderer->valid = 0; return 1; } return 0; } void json_renderer_begin(json_renderer_t *renderer, json_element_type_t type) { const char *towrite; char next_level; if (!renderer || !renderer->valid) return; if (renderer->level == MAX_RECURSION) { renderer->valid = 0; return; } switch (type) { case JSON_ELEMENT_TYPE_OBJECT: next_level = 'o'; towrite = "{"; break; case JSON_ELEMENT_TYPE_ARRAY: next_level = 'a'; towrite = "["; break; default: renderer->valid = 0; return; } write_raw(renderer, towrite, 1); renderer->levelinfo[renderer->level++] = next_level; } void json_renderer_end(json_renderer_t *renderer) { if (!renderer || !renderer->valid) return; switch (renderer->levelinfo[renderer->level-1]) { case 'o': case 'O': write_raw(renderer, "}", 0); renderer->level--; break; case 'a': case 'A': write_raw(renderer, "]", 0); renderer->level--; break; default: renderer->valid = 0; break; } } void json_renderer_write_null(json_renderer_t *renderer) { if (want_write_value(renderer) != 0) return; write_raw(renderer, "null", 1); } void json_renderer_write_boolean(json_renderer_t *renderer, int val) { if (want_write_value(renderer) != 0) return; write_raw(renderer, val ? "true" : "false", 1); } static void write_string(json_renderer_t *renderer, const char *string, unsigned int flags) { // Allocate for the quotes plus 110% of the string length to account for escapes. if (allocate_buffer(renderer, 2 + ((strlen(string) * 110) / 100)) != 0) return; write_raw(renderer, "\"", 1); for (; *string && renderer->valid; string++) { if (renderer->bufferfill == renderer->bufferlen) { // Reallocate buffer, same rules as above, expect that we only need one additional quote. if (allocate_buffer(renderer, 1 + ((strlen(string) * 110) / 100)) != 0) return; } if (((unsigned char)*string) < 0x20) { char buf[7]; snprintf(buf, sizeof(buf), "\\u%.4x", (unsigned int)*string); write_raw(renderer, buf, 0); } else if (*string == '\\' || *string == '"') { if (allocate_buffer(renderer, 2) != 0) return; renderer->buffer[renderer->bufferfill++] = '\\'; renderer->buffer[renderer->bufferfill++] = *string; } else { renderer->buffer[renderer->bufferfill++] = *string; } } write_raw(renderer, "\"", 0); } void json_renderer_write_key(json_renderer_t *renderer, const char *key, unsigned int flags) { char level; if (!renderer || !renderer->valid) return; level = renderer->levelinfo[renderer->level-1]; if (level != 'o' && level != 'O') { renderer->valid = 0; return; } if (renderer->level == MAX_RECURSION) { renderer->valid = 0; return; } write_string(renderer, key, flags); renderer->levelinfo[renderer->level++] = 'P'; } void json_renderer_write_string(json_renderer_t *renderer, const char *string, unsigned int flags) { if (want_write_value(renderer) != 0) return; write_string(renderer, string, flags); } void json_renderer_write_int(json_renderer_t *renderer, intmax_t val) { char buf[80]; if (want_write_value(renderer) != 0) return; snprintf(buf, sizeof(buf), "%" PRIdMAX, val); write_raw(renderer, buf, 1); } void json_renderer_write_uint(json_renderer_t *renderer, uintmax_t val) { char buf[80]; if (want_write_value(renderer) != 0) return; snprintf(buf, sizeof(buf), "%" PRIuMAX, val); write_raw(renderer, buf, 1); }