libjson_la_SOURCES = \
json-syntax.c \
json-types.c \
- json-tree.new.c \
+ json-tree.c \
json-tree-io.c \
- json-parser.new.c \
+ json-parser.c \
json-generator.c \
json-istream.c \
json-ostream.c \
headers = \
json-syntax.h \
json-types.h \
- json-tree.new.h \
+ json-tree.h \
json-tree-io.h \
- json-parser.new.h \
+ json-parser.h \
json-generator.h \
json-istream.h \
json-ostream.h \
#include "json-types.h"
-#define json_append_escaped json_append_escaped_new
-#define json_append_escaped_data json_append_escaped_data_new
-
// FIXME: add settings for formatting/indenting the output
struct json_generator;
#ifndef JSON_ISTREAM_H
#define JSON_ISTREAM_H
-#include "json-tree.new.h"
-#include "json-parser.new.h"
+#include "json-tree.h"
+#include "json-parser.h"
// FIXME: don't bother recording values if we're only validating/skipping
#include "lib.h"
#include "json-types.h"
-#include "json-tree.new.h"
+#include "json-tree.h"
#include "json-generator.h"
struct json_ostream;
#include "istream-private.h"
#include "json-syntax.h"
-#include "json-parser.new.h"
+#include "json-parser.h"
#include <stdlib.h>
#include <math.h>
#include "json-types.h"
-#define json_parser_init json_parser_new_init
-#define json_parser_deinit json_parser_new_deinit
-
// FIXME: don't bother recording values if we're only validating.
/*
#ifndef JSON_TEXT_H
#define JSON_TEXT_H
-#include "json-parser.new.h"
+#include "json-parser.h"
#include "json-generator.h"
int json_text_format_data(const void *data, size_t size,
#ifndef JSON_TREE_IO_H
#define JSON_TREE_IO_H
-#include "json-tree.new.h"
-#include "json-parser.new.h"
+#include "json-tree.h"
+#include "json-parser.h"
#include "json-generator.h"
/*
#include "array.h"
#include "istream.h"
-#include "json-tree.new.h"
+#include "json-tree.h"
struct json_tree_node_list {
struct json_tree_node *head, *tail;
#include "iostream-pump.h"
#include "test-common.h"
-#include "json-parser.new.h"
+#include "json-parser.h"
#include "json-generator.h"
#include "json-istream.h"
#include "json-ostream.h"
#include "istream-base64.h"
#include "test-common.h"
-#include "json-parser.new.h"
+#include "json-parser.h"
#include <unistd.h>
#include "ostream.h"
#include "test-common.h"
-#include "json-tree.new.h"
+#include "json-tree.h"
#include <unistd.h>
#include "hash-method.h"
#include "istream.h"
#include "iso8601-date.h"
-#include "json-tree.new.h"
+#include "json-tree.h"
#include "array.h"
#include "base64.h"
#include "str-sanitize.h"
istream-failure-at.c \
istream-file.c \
istream-hash.c \
- istream-jsonstr.c \
istream-limit.c \
istream-multiplex.c \
istream-nonuls.c \
ioloop-select.c \
ioloop-epoll.c \
ioloop-kqueue.c \
- json-parser.c \
- json-tree.c \
lib.c \
lib-event.c \
lib-signals.c \
istream-failure-at.h \
istream-file-private.h \
istream-hash.h \
- istream-jsonstr.h \
istream-multiplex.h \
istream-nonuls.h \
istream-private.h \
ioloop-iolist.h \
ioloop-private.h \
ioloop-notify-fd.h \
- json-parser.h \
- json-tree.h \
lib.h \
lib-event.h \
lib-event-private.h \
test-istream-concat.c \
test-istream-crlf.c \
test-istream-failure-at.c \
- test-istream-jsonstr.c \
test-istream-multiplex.c \
test-istream-noop.c \
test-istream-seekable.c \
test-istream-tee.c \
test-istream-try.c \
test-istream-unix.c \
- test-json-parser.c \
- test-json-tree.c \
test-lib-event.c \
test-lib-signals.c \
test-llist.c \
+++ /dev/null
-/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "buffer.h"
-#include "hex-dec.h"
-#include "unichar.h"
-#include "istream-private.h"
-#include "istream-jsonstr.h"
-
-#define MAX_UTF8_LEN 6
-
-struct jsonstr_istream {
- struct istream_private istream;
-
- /* The end '"' was found */
- bool str_end:1;
-};
-
-static int
-i_stream_jsonstr_read_parent(struct jsonstr_istream *jstream,
- unsigned int min_bytes)
-{
- struct istream_private *stream = &jstream->istream;
- size_t size, avail;
- ssize_t ret;
-
- size = i_stream_get_data_size(stream->parent);
- while (size < min_bytes) {
- ret = i_stream_read_memarea(stream->parent);
- if (ret <= 0) {
- if (ret == -2) {
- /* tiny parent buffer size - shouldn't happen */
- return -2;
- }
- stream->istream.stream_errno =
- stream->parent->stream_errno;
- stream->istream.eof = stream->parent->eof;
- if (ret == -1 && stream->istream.stream_errno == 0) {
- io_stream_set_error(&stream->iostream,
- "EOF before trailing <\"> was seen");
- stream->istream.stream_errno = EPIPE;
- }
- return ret;
- }
- size = i_stream_get_data_size(stream->parent);
- }
-
- if (!i_stream_try_alloc(stream, size, &avail))
- return -2;
- return 1;
-}
-
-static int
-i_stream_json_unescape(const unsigned char *src, size_t len,
- unsigned char *dest,
- unsigned int *src_size_r, unsigned int *dest_size_r)
-{
- switch (*src) {
- case '"':
- case '\\':
- case '/':
- *dest = *src;
- break;
- case 'b':
- *dest = '\b';
- break;
- case 'f':
- *dest = '\f';
- break;
- case 'n':
- *dest = '\n';
- break;
- case 'r':
- *dest = '\r';
- break;
- case 't':
- *dest = '\t';
- break;
- case 'u': {
- char chbuf[5] = {0};
- unichar_t chr,chr2 = 0;
- buffer_t buf;
- if (len < 5)
- return 5;
- buffer_create_from_data(&buf, dest, MAX_UTF8_LEN);
- memcpy(chbuf, src+1, 4);
- if (str_to_uint32_hex(chbuf, &chr)<0)
- return -1;
- if (UTF16_VALID_LOW_SURROGATE(chr))
- return -1;
- /* if we encounter surrogate, we need another \\uxxxx */
- if (UTF16_VALID_HIGH_SURROGATE(chr)) {
- if (len < 5+2+4)
- return 5+2+4;
- if (src[5] != '\\' && src[6] != 'u')
- return -1;
- memcpy(chbuf, src+7, 4);
- if (str_to_uint32_hex(chbuf, &chr2)<0)
- return -1;
- if (!UTF16_VALID_LOW_SURROGATE(chr2))
- return -1;
- chr = uni_join_surrogate(chr, chr2);
- }
- if (!uni_is_valid_ucs4(chr))
- return -1;
- uni_ucs4_to_utf8_c(chr, &buf);
- *src_size_r = 5 + (chr2>0?6:0);
- *dest_size_r = buf.used;
- return 0;
- }
- default:
- return -1;
- }
- *src_size_r = 1;
- *dest_size_r = 1;
- return 0;
-}
-
-static ssize_t i_stream_jsonstr_read(struct istream_private *stream)
-{
- struct jsonstr_istream *jstream =
- container_of(stream, struct jsonstr_istream, istream);
- const unsigned char *data;
- unsigned int srcskip, destskip, extra;
- size_t i, dest, size;
- ssize_t ret, ret2;
-
- if (jstream->str_end) {
- stream->istream.eof = TRUE;
- return -1;
- }
-
- ret = i_stream_jsonstr_read_parent(jstream, 1);
- if (ret <= 0)
- return ret;
-
- /* @UNSAFE */
- dest = stream->pos;
- extra = 0;
-
- data = i_stream_get_data(stream->parent, &size);
- for (i = 0; i < size && dest < stream->buffer_size; ) {
- if (data[i] == '"') {
- jstream->str_end = TRUE;
- if (dest == stream->pos) {
- stream->istream.eof = TRUE;
- return -1;
- }
- break;
- } else if (data[i] == '\\') {
- if (i+1 == size) {
- /* not enough input for \x */
- extra = 1;
- break;
- }
- if (data[i+1] == 'u' && stream->buffer_size - dest < MAX_UTF8_LEN) {
- /* UTF8 output is max. 6 chars */
- if (dest == stream->pos)
- return -2;
- break;
- }
- i++;
- if ((ret2 = i_stream_json_unescape(data + i, size - i,
- stream->w_buffer + dest,
- &srcskip, &destskip)) < 0) {
- /* invalid string */
- io_stream_set_error(&stream->iostream,
- "Invalid JSON string");
- stream->istream.stream_errno = EINVAL;
- return -1;
- } else if (ret2 > 0) {
- /* we need to get more bytes, do not consume
- escape slash */
- i--;
- extra = ret2;
- break;
- }
- i += srcskip;
- i_assert(i <= size);
- dest += destskip;
- i_assert(dest <= stream->buffer_size);
- } else {
- stream->w_buffer[dest++] = data[i];
- i++;
- }
- }
- i_stream_skip(stream->parent, i);
-
- ret = dest - stream->pos;
- if (ret == 0) {
- /* not enough input */
- i_assert(i == 0);
- i_assert(extra > 0);
- ret = i_stream_jsonstr_read_parent(jstream, extra+1);
- if (ret <= 0)
- return ret;
- return i_stream_jsonstr_read(stream);
- }
- i_assert(ret > 0);
- stream->pos = dest;
- return ret;
-}
-
-struct istream *i_stream_create_jsonstr(struct istream *input)
-{
- struct jsonstr_istream *dstream;
-
- dstream = i_new(struct jsonstr_istream, 1);
- dstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
- dstream->istream.read = i_stream_jsonstr_read;
-
- dstream->istream.istream.readable_fd = FALSE;
- dstream->istream.istream.blocking = input->blocking;
- dstream->istream.istream.seekable = FALSE;
- return i_stream_create(&dstream->istream, input,
- i_stream_get_fd(input), 0);
-}
+++ /dev/null
-#ifndef ISTREAM_JSONSTR_H
-#define ISTREAM_JSONSTR_H
-
-/* Parse input until '"' is reached. Unescape JSON \x codes. */
-struct istream *i_stream_create_jsonstr(struct istream *input);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "istream.h"
-#include "hex-dec.h"
-#include "unichar.h"
-#include "istream-jsonstr.h"
-#include "json-parser.h"
-
-enum json_state {
- JSON_STATE_ROOT = 0,
- JSON_STATE_OBJECT_OPEN,
- JSON_STATE_OBJECT_KEY,
- JSON_STATE_OBJECT_COLON,
- JSON_STATE_OBJECT_VALUE,
- JSON_STATE_OBJECT_SKIP_STRING,
- JSON_STATE_OBJECT_NEXT,
- JSON_STATE_ARRAY_OPEN,
- JSON_STATE_ARRAY_VALUE,
- JSON_STATE_ARRAY_SKIP_STRING,
- JSON_STATE_ARRAY_NEXT,
- JSON_STATE_ARRAY_NEXT_SKIP,
- JSON_STATE_VALUE,
- JSON_STATE_DONE
-};
-
-struct json_parser {
- pool_t pool;
- struct istream *input;
- uoff_t highwater_offset;
- enum json_parser_flags flags;
-
- const unsigned char *start, *end, *data;
- const char *error;
- string_t *value;
- struct istream *strinput;
-
- enum json_state state;
- ARRAY(enum json_state) nesting;
- unsigned int nested_skip_count;
-
- bool skipping;
- bool seen_eof;
-};
-
-static int json_parser_read_more(struct json_parser *parser)
-{
- uoff_t cur_highwater = parser->input->v_offset +
- i_stream_get_data_size(parser->input);
- size_t size;
- ssize_t ret;
-
- i_assert(parser->highwater_offset <= cur_highwater);
-
- if (parser->error != NULL)
- return -1;
-
- if (parser->highwater_offset == cur_highwater) {
- ret = i_stream_read(parser->input);
- if (ret == -2) {
- parser->error = "Token too large";
- return -1;
- }
- if (ret < 0 && !parser->seen_eof &&
- i_stream_get_data_size(parser->input) > 0 &&
- parser->input->stream_errno == 0) {
- /* call it once more to finish any pending number */
- parser->seen_eof = TRUE;
- } else if (ret <= 0) {
- return ret;
- } else {
- cur_highwater = parser->input->v_offset +
- i_stream_get_data_size(parser->input);
- i_assert(parser->highwater_offset < cur_highwater);
- parser->highwater_offset = cur_highwater;
- }
- }
-
- parser->start = parser->data = i_stream_get_data(parser->input, &size);
- parser->end = parser->start + size;
- i_assert(size > 0);
- return 1;
-}
-
-static void json_parser_update_input_pos(struct json_parser *parser)
-{
- size_t size;
-
- if (parser->data == parser->start)
- return;
-
- i_stream_skip(parser->input, parser->data - parser->start);
- parser->start = parser->data = i_stream_get_data(parser->input, &size);
- parser->end = parser->start + size;
- if (size > 0) {
- /* we skipped over some data and there's still data left.
- no need to read() the next time. */
- parser->highwater_offset = 0;
- } else {
- parser->highwater_offset = parser->input->v_offset;
- }
-}
-
-struct json_parser *json_parser_init(struct istream *input)
-{
- return json_parser_init_flags(input, 0);
-}
-
-struct json_parser *json_parser_init_flags(struct istream *input,
- enum json_parser_flags flags)
-{
- struct json_parser *parser;
- pool_t pool = pool_alloconly_create("json parser",
- sizeof(struct json_parser)+64);
-
- parser = p_new(pool, struct json_parser, 1);
- parser->pool = pool;
- parser->input = input;
- parser->flags = flags;
- parser->value = str_new(default_pool, 128);
- i_array_init(&parser->nesting, 8);
- i_stream_ref(input);
-
- if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
- parser->state = JSON_STATE_VALUE;
- return parser;
-}
-
-int json_parser_deinit(struct json_parser **_parser, const char **error_r)
-{
- struct json_parser *parser = *_parser;
-
- *_parser = NULL;
-
- if (parser->error != NULL) {
- /* actual parser error */
- *error_r = t_strdup(parser->error);
- } else if (parser->input->stream_errno != 0) {
- *error_r = t_strdup_printf("read(%s) failed: %s",
- i_stream_get_name(parser->input),
- i_stream_get_error(parser->input));
- } else if (parser->data == parser->end &&
- parser->state != JSON_STATE_DONE) {
- *error_r = "Missing '}'";
- } else {
- *error_r = NULL;
- }
-
- i_stream_unref(&parser->input);
- array_free(&parser->nesting);
- str_free(&parser->value);
- pool_unref(&parser->pool);
- return *error_r != NULL ? -1 : 0;
-}
-
-static bool json_parse_whitespace(struct json_parser *parser)
-{
- for (; parser->data != parser->end; parser->data++) {
- switch (*parser->data) {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- break;
- default:
- json_parser_update_input_pos(parser);
- return TRUE;
- }
- }
- json_parser_update_input_pos(parser);
- return FALSE;
-}
-
-static int json_skip_string(struct json_parser *parser)
-{
- for (; parser->data != parser->end; parser->data++) {
- if (*parser->data == '"') {
- parser->data++;
- json_parser_update_input_pos(parser);
- return 1;
- }
- if (*parser->data == '\\') {
- parser->data++;
- if (parser->data == parser->end)
- break;
- switch (*parser->data) {
- case '"':
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- break;
- case 'u':
- if (parser->end - parser->data < 4) {
- parser->data = parser->end;
- return -1;
- }
- parser->data += 3;
- break;
- default:
- parser->error = "Invalid escape string";
- return -1;
- }
- }
- }
- json_parser_update_input_pos(parser);
- return 0;
-}
-
-static int json_parse_unicode_escape(struct json_parser *parser)
-{
- char chbuf[5] = {0};
- unichar_t chr, hi_surg;
-
- parser->data++;
- if (parser->end - parser->data < 4) {
- /* wait for more data */
- parser->data = parser->end;
- return 0;
- }
- memcpy(chbuf, parser->data, 4);
- if (str_to_uint32_hex(chbuf, &chr) < 0) {
- parser->error = "Invalid unicode escape seen";
- return -1;
- }
- if (UTF16_VALID_HIGH_SURROGATE(chr)) {
- /* possible surrogate pair */
- hi_surg = chr;
- chr = 0;
- parser->data += 4;
- if (parser->data >= parser->end) {
- /* wait for more data */
- parser->data = parser->end;
- return 0;
- }
- if ((parser->end - parser->data) < 2) {
- if (parser->data[0] == '\\') {
- /* wait for more data */
- parser->data = parser->end;
- return 0;
- }
- /* error */
- }
- if ((parser->end - parser->data) < 6) {
- if (parser->data[0] == '\\' &&
- parser->data[1] == 'u') {
- /* wait for more data */
- parser->data = parser->end;
- return 0;
- }
- /* error */
- } else {
- memcpy(chbuf, &parser->data[2], 4);
- if (str_to_uint32_hex(chbuf, &chr) < 0) {
- parser->error = "Invalid unicode escape seen";
- return -1;
- }
- }
- if (parser->data[0] != '\\' || parser->data[1] != 'u' ||
- !UTF16_VALID_LOW_SURROGATE(chr)) {
- parser->error = p_strdup_printf(parser->pool,
- "High surrogate 0x%04x seen, "
- "but not followed by low surrogate", hi_surg);
- return -1;
- }
- chr = uni_join_surrogate(hi_surg, chr);
- parser->data += 2;
- }
-
- if (!uni_is_valid_ucs4(chr)) {
- parser->error = p_strdup_printf(parser->pool,
- "Invalid unicode character U+%04x", chr);
- return -1;
- }
- if (chr == 0) {
- parser->error = "\\u0000 not supported in strings";
- return -1;
- }
- uni_ucs4_to_utf8_c(chr, parser->value);
- parser->data += 3;
- return 1;
-}
-
-static int json_parse_string(struct json_parser *parser, bool allow_skip,
- const char **value_r)
-{
- int ret;
-
- if (*parser->data != '"')
- return -1;
- parser->data++;
-
- if (parser->skipping && allow_skip) {
- *value_r = NULL;
- return json_skip_string(parser);
- }
-
- str_truncate(parser->value, 0);
- for (; parser->data != parser->end; parser->data++) {
- if (*parser->data == '"') {
- parser->data++;
- *value_r = str_c(parser->value);
- return 1;
- }
- switch (*parser->data) {
- case '\\':
- if (++parser->data == parser->end)
- return 0;
- switch (*parser->data) {
- case '"':
- case '\\':
- case '/':
- str_append_c(parser->value, *parser->data);
- break;
- case 'b':
- str_append_c(parser->value, '\b');
- break;
- case 'f':
- str_append_c(parser->value, '\f');
- break;
- case 'n':
- str_append_c(parser->value, '\n');
- break;
- case 'r':
- str_append_c(parser->value, '\r');
- break;
- case 't':
- str_append_c(parser->value, '\t');
- break;
- case 'u':
- if ((ret=json_parse_unicode_escape(parser)) <= 0)
- return ret;
- break;
- default:
- parser->error = "Invalid escape string";
- return -1;
- }
- break;
- case '\0':
- parser->error = "NULs not supported in strings";
- return -1;
- default:
- str_append_c(parser->value, *parser->data);
- break;
- }
- }
- return 0;
-}
-
-static int
-json_parse_digits(struct json_parser *parser)
-{
- if (parser->data == parser->end)
- return 0;
- if (*parser->data < '0' || *parser->data > '9')
- return -1;
-
- while (parser->data != parser->end &&
- *parser->data >= '0' && *parser->data <= '9')
- str_append_c(parser->value, *parser->data++);
- return 1;
-}
-
-static int json_parse_int(struct json_parser *parser)
-{
- int ret;
-
- if (*parser->data == '-') {
- str_append_c(parser->value, *parser->data++);
- if (parser->data == parser->end)
- return 0;
- }
- if (*parser->data == '0')
- str_append_c(parser->value, *parser->data++);
- else {
- if ((ret = json_parse_digits(parser)) <= 0)
- return ret;
- }
- return 1;
-}
-
-static int json_parse_number(struct json_parser *parser, const char **value_r)
-{
- int ret;
-
- str_truncate(parser->value, 0);
- if ((ret = json_parse_int(parser)) <= 0)
- return ret;
- if (parser->data != parser->end && *parser->data == '.') {
- /* frac */
- str_append_c(parser->value, *parser->data++);
- if ((ret = json_parse_digits(parser)) <= 0)
- return ret;
- }
- if (parser->data != parser->end &&
- (*parser->data == 'e' || *parser->data == 'E')) {
- /* exp */
- str_append_c(parser->value, *parser->data++);
- if (parser->data == parser->end)
- return 0;
- if (*parser->data == '+' || *parser->data == '-')
- str_append_c(parser->value, *parser->data++);
- if ((ret = json_parse_digits(parser)) <= 0)
- return ret;
- }
- if (parser->data == parser->end && !parser->input->eof)
- return 0;
- *value_r = str_c(parser->value);
- return 1;
-}
-
-static int json_parse_atom(struct json_parser *parser, const char *atom)
-{
- size_t avail, len = strlen(atom);
-
- avail = parser->end - parser->data;
- if (avail < len) {
- if (memcmp(parser->data, atom, avail) != 0)
- return -1;
-
- /* everything matches so far, but we need more data */
- parser->data += avail;
- return 0;
- }
- if (memcmp(parser->data, atom, len) != 0)
- return -1;
- parser->data += len;
- return 1;
-}
-
-static int json_parse_denest(struct json_parser *parser)
-{
- const enum json_state *nested_states;
- unsigned int count;
-
- parser->data++;
- json_parser_update_input_pos(parser);
-
- nested_states = array_get(&parser->nesting, &count);
- i_assert(count > 0);
- if (count == 1) {
- /* closing root */
- parser->state = JSON_STATE_DONE;
- if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
- return 0;
- /* we want to return the ending "]" or "}" to caller */
- return 1;
- }
-
- /* closing a nested object */
- parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ?
- JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
- array_delete(&parser->nesting, count-1, 1);
-
- if (parser->nested_skip_count > 0) {
- parser->nested_skip_count--;
- return 0;
- }
- return 1;
-}
-
-static int
-json_parse_close_object(struct json_parser *parser, enum json_type *type_r)
-{
- if (json_parse_denest(parser) == 0)
- return 0;
- *type_r = JSON_TYPE_OBJECT_END;
- return 1;
-}
-
-static int
-json_parse_close_array(struct json_parser *parser, enum json_type *type_r)
-{
- if (json_parse_denest(parser) == 0)
- return 0;
- *type_r = JSON_TYPE_ARRAY_END;
- return 1;
-}
-
-static void json_parser_object_open(struct json_parser *parser)
-{
- parser->data++;
- parser->state = JSON_STATE_OBJECT_OPEN;
- array_push_back(&parser->nesting, &parser->state);
- json_parser_update_input_pos(parser);
-}
-
-static int
-json_try_parse_next(struct json_parser *parser, enum json_type *type_r,
- const char **value_r)
-{
- bool skipping = parser->skipping;
- int ret;
-
- if (!json_parse_whitespace(parser))
- return -1;
-
- switch (parser->state) {
- case JSON_STATE_ROOT:
- if (*parser->data != '{') {
- parser->error = "Object doesn't begin with '{'";
- return -1;
- }
- json_parser_object_open(parser);
- return 0;
- case JSON_STATE_OBJECT_VALUE:
- case JSON_STATE_ARRAY_VALUE:
- case JSON_STATE_VALUE:
- if (*parser->data == '{') {
- json_parser_object_open(parser);
-
- if (parser->skipping) {
- parser->nested_skip_count++;
- return 0;
- }
- *type_r = JSON_TYPE_OBJECT;
- return 1;
- } else if (*parser->data == '[') {
- parser->data++;
- parser->state = JSON_STATE_ARRAY_OPEN;
- array_push_back(&parser->nesting, &parser->state);
- json_parser_update_input_pos(parser);
-
- if (parser->skipping) {
- parser->nested_skip_count++;
- return 0;
- }
- *type_r = JSON_TYPE_ARRAY;
- return 1;
- }
-
- if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
- *type_r = JSON_TYPE_STRING;
- } else if ((ret = json_parse_number(parser, value_r)) >= 0) {
- *type_r = JSON_TYPE_NUMBER;
- } else if ((ret = json_parse_atom(parser, "true")) >= 0) {
- *type_r = JSON_TYPE_TRUE;
- *value_r = "true";
- } else if ((ret = json_parse_atom(parser, "false")) >= 0) {
- *type_r = JSON_TYPE_FALSE;
- *value_r = "false";
- } else if ((ret = json_parse_atom(parser, "null")) >= 0) {
- *type_r = JSON_TYPE_NULL;
- *value_r = NULL;
- } else {
- if (parser->error == NULL)
- parser->error = "Invalid data as value";
- return -1;
- }
- if (ret == 0) {
- i_assert(parser->data == parser->end);
- if (parser->skipping && *type_r == JSON_TYPE_STRING) {
- /* a large string that we want to skip over. */
- json_parser_update_input_pos(parser);
- parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
- JSON_STATE_OBJECT_SKIP_STRING :
- JSON_STATE_ARRAY_SKIP_STRING;
- return 0;
- }
- return -1;
- }
- switch (parser->state) {
- case JSON_STATE_OBJECT_VALUE:
- parser->state = JSON_STATE_OBJECT_NEXT;
- break;
- case JSON_STATE_ARRAY_VALUE:
- parser->state = JSON_STATE_ARRAY_NEXT;
- break;
- case JSON_STATE_VALUE:
- parser->state = JSON_STATE_DONE;
- break;
- default:
- i_unreached();
- }
- break;
- case JSON_STATE_OBJECT_OPEN:
- if (*parser->data == '}')
- return json_parse_close_object(parser, type_r);
- parser->state = JSON_STATE_OBJECT_KEY;
- /* fall through */
- case JSON_STATE_OBJECT_KEY:
- if (json_parse_string(parser, FALSE, value_r) <= 0) {
- parser->error = "Expected string as object key";
- return -1;
- }
- *type_r = JSON_TYPE_OBJECT_KEY;
- parser->state = JSON_STATE_OBJECT_COLON;
- break;
- case JSON_STATE_OBJECT_COLON:
- if (*parser->data != ':') {
- parser->error = "Expected ':' after key";
- return -1;
- }
- parser->data++;
- parser->state = JSON_STATE_OBJECT_VALUE;
- json_parser_update_input_pos(parser);
- return 0;
- case JSON_STATE_OBJECT_NEXT:
- if (parser->skipping && parser->nested_skip_count == 0) {
- /* we skipped over the previous value */
- parser->skipping = FALSE;
- }
- if (*parser->data == '}')
- return json_parse_close_object(parser, type_r);
- if (*parser->data != ',') {
- parser->error = "Expected ',' or '}' after object value";
- return -1;
- }
- parser->state = JSON_STATE_OBJECT_KEY;
- parser->data++;
- json_parser_update_input_pos(parser);
- return 0;
- case JSON_STATE_ARRAY_OPEN:
- if (*parser->data == ']')
- return json_parse_close_array(parser, type_r);
- parser->state = JSON_STATE_ARRAY_VALUE;
- return 0;
- case JSON_STATE_ARRAY_NEXT:
- if (parser->skipping && parser->nested_skip_count == 0) {
- /* we skipped over the previous value */
- parser->skipping = FALSE;
- }
- /* fall through */
- case JSON_STATE_ARRAY_NEXT_SKIP:
- if (*parser->data == ']')
- return json_parse_close_array(parser, type_r);
- if (*parser->data != ',') {
- parser->error = "Expected ',' or '}' after array value";
- return -1;
- }
- parser->state = JSON_STATE_ARRAY_VALUE;
- parser->data++;
- json_parser_update_input_pos(parser);
- return 0;
- case JSON_STATE_OBJECT_SKIP_STRING:
- case JSON_STATE_ARRAY_SKIP_STRING:
- if (json_skip_string(parser) <= 0)
- return -1;
- parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ?
- JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
- return 0;
- case JSON_STATE_DONE:
- parser->error = "Unexpected data at the end";
- return -1;
- }
- json_parser_update_input_pos(parser);
- return skipping ? 0 : 1;
-}
-
-int json_parse_next(struct json_parser *parser, enum json_type *type_r,
- const char **value_r)
-{
- int ret;
-
- i_assert(parser->strinput == NULL);
-
- *value_r = NULL;
-
- while ((ret = json_parser_read_more(parser)) > 0) {
- while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0)
- ;
- if (ret > 0)
- break;
- if (parser->data != parser->end)
- return -1;
- /* parsing probably failed because there wasn't enough input.
- reset the error and try reading more. */
- parser->error = NULL;
- parser->highwater_offset = parser->input->v_offset +
- i_stream_get_data_size(parser->input);
- }
- return ret;
-}
-
-void json_parse_skip_next(struct json_parser *parser)
-{
- i_assert(!parser->skipping);
- i_assert(parser->strinput == NULL);
- i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
- parser->state == JSON_STATE_OBJECT_VALUE ||
- parser->state == JSON_STATE_ARRAY_VALUE ||
- parser->state == JSON_STATE_ARRAY_NEXT);
-
- parser->skipping = TRUE;
- if (parser->state == JSON_STATE_ARRAY_NEXT)
- parser->state = JSON_STATE_ARRAY_NEXT_SKIP;
-}
-
-void json_parse_skip(struct json_parser *parser)
-{
- i_assert(!parser->skipping);
- i_assert(parser->strinput == NULL);
- i_assert(parser->state == JSON_STATE_OBJECT_NEXT ||
- parser->state == JSON_STATE_OBJECT_OPEN ||
- parser->state == JSON_STATE_ARRAY_NEXT ||
- parser->state == JSON_STATE_ARRAY_OPEN);
-
- if (parser->state == JSON_STATE_OBJECT_OPEN ||
- parser->state == JSON_STATE_ARRAY_OPEN)
- parser->nested_skip_count++;
-
- parser->skipping = TRUE;
- if (parser->state == JSON_STATE_ARRAY_NEXT)
- parser->state = JSON_STATE_ARRAY_NEXT_SKIP;
-}
-
-static void json_strinput_destroyed(struct json_parser *parser)
-{
- i_assert(parser->strinput != NULL);
-
- parser->strinput = NULL;
-}
-
-static int
-json_try_parse_stream_start(struct json_parser *parser,
- struct istream **input_r)
-{
- if (!json_parse_whitespace(parser))
- return -1;
-
- if (parser->state == JSON_STATE_OBJECT_COLON) {
- if (*parser->data != ':') {
- parser->error = "Expected ':' after key";
- return -1;
- }
- parser->data++;
- parser->state = JSON_STATE_OBJECT_VALUE;
- if (!json_parse_whitespace(parser))
- return -1;
- }
-
- if (*parser->data != '"')
- return -1;
- parser->data++;
- json_parser_update_input_pos(parser);
-
- parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
- JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING;
- parser->strinput = i_stream_create_jsonstr(parser->input);
- i_stream_add_destroy_callback(parser->strinput,
- json_strinput_destroyed, parser);
-
- *input_r = parser->strinput;
- return 0;
-}
-
-int json_parse_next_stream(struct json_parser *parser,
- struct istream **input_r)
-{
- int ret;
-
- i_assert(!parser->skipping);
- i_assert(parser->strinput == NULL);
- i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
- parser->state == JSON_STATE_OBJECT_VALUE ||
- parser->state == JSON_STATE_ARRAY_VALUE);
-
- *input_r = NULL;
-
- while ((ret = json_parser_read_more(parser)) > 0) {
- if (json_try_parse_stream_start(parser, input_r) == 0)
- break;
- if (parser->data != parser->end)
- return -1;
- /* parsing probably failed because there wasn't enough input.
- reset the error and try reading more. */
- parser->error = NULL;
- parser->highwater_offset = parser->input->v_offset +
- i_stream_get_data_size(parser->input);
- }
- return ret;
-}
-
-static void json_append_escaped_char(string_t *dest, unsigned char src)
-{
- switch (src) {
- case '\b':
- str_append(dest, "\\b");
- break;
- case '\f':
- str_append(dest, "\\f");
- break;
- case '\n':
- str_append(dest, "\\n");
- break;
- case '\r':
- str_append(dest, "\\r");
- break;
- case '\t':
- str_append(dest, "\\t");
- break;
- case '"':
- str_append(dest, "\\\"");
- break;
- case '\\':
- str_append(dest, "\\\\");
- break;
- default:
- if (src < 0x20 || src >= 0x80)
- str_printfa(dest, "\\u%04x", src);
- else
- str_append_c(dest, src);
- break;
- }
-}
-
-void json_append_escaped_ucs4(string_t *dest, unichar_t chr)
-{
- if (chr < 0x80)
- json_append_escaped_char(dest, (unsigned char)chr);
- else if (chr == 0x2028 || chr == 0x2029)
- str_printfa(dest, "\\u%04x", chr);
- else
- uni_ucs4_to_utf8_c(chr, dest);
-}
-
-void ostream_escaped_json_format(string_t *dest, unsigned char src)
-{
- json_append_escaped_char(dest, src);
-}
-
-void json_append_escaped(string_t *dest, const char *src)
-{
- json_append_escaped_data(dest, (const unsigned char*)src, strlen(src));
-}
-
-void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size)
-{
- size_t i;
- int bytes = 0;
- unichar_t chr;
-
- for (i = 0; i < size;) {
- bytes = uni_utf8_get_char_n(src+i, size-i, &chr);
- if (bytes > 0 && uni_is_valid_ucs4(chr)) {
- json_append_escaped_ucs4(dest, chr);
- i += bytes;
- } else {
- str_append_data(dest, UNICODE_REPLACEMENT_CHAR_UTF8,
- UTF8_REPLACEMENT_CHAR_LEN);
- i++;
- }
- }
-}
+++ /dev/null
-#ifndef JSON_PARSER_H
-#define JSON_PARSER_H
-
-#include "unichar.h"
-
-enum json_type {
- /* { key: */
- JSON_TYPE_OBJECT_KEY,
- /* : { new object */
- JSON_TYPE_OBJECT,
- /* } (not returned for the root object) */
- JSON_TYPE_OBJECT_END,
-
- JSON_TYPE_ARRAY,
- JSON_TYPE_ARRAY_END,
-
- JSON_TYPE_STRING,
- JSON_TYPE_NUMBER,
- JSON_TYPE_TRUE,
- JSON_TYPE_FALSE,
- JSON_TYPE_NULL
-};
-
-enum json_parser_flags {
- /* By default we assume that the input is an object and parsing skips
- the root level "{" and "}". If this flag is set, it's possible to
- parse any other type of JSON values directly. */
- JSON_PARSER_NO_ROOT_OBJECT = 0x01
-};
-
-/* Parse JSON tokens from the input stream. */
-struct json_parser *json_parser_init(struct istream *input);
-struct json_parser *json_parser_init_flags(struct istream *input,
- enum json_parser_flags flags);
-
-int json_parser_deinit(struct json_parser **parser, const char **error_r);
-
-/* Parse the next token. Returns 1 if found, 0 if more input stream is
- non-blocking and needs more input, -1 if input stream is at EOF. */
-int json_parse_next(struct json_parser *parser, enum json_type *type_r,
- const char **value_r);
-/* Skip the next object value. If it's an object, its members are also
- skipped. */
-void json_parse_skip_next(struct json_parser *parser);
-/* Skip the remainder of the value parsed earlier by json_parse_next(). */
-void json_parse_skip(struct json_parser *parser);
-/* Return the following string as input stream. Returns 1 if ok, 0 if
- input stream is non-blocking and needs more input, -1 if the next token
- isn't a string (call json_parse_next()). */
-int json_parse_next_stream(struct json_parser *parser,
- struct istream **input_r);
-
-/* Append UCS4 to already opened JSON string. */
-void json_append_escaped_ucs4(string_t *dest, unichar_t chr);
-/* Append data to already opened JSON string. src should be valid UTF-8 data. */
-void json_append_escaped(string_t *dest, const char *src);
-/* Same as json_append_escaped(), but append non-\0 terminated input. */
-void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size);
-void ostream_escaped_json_format(string_t *dest, unsigned char src);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "json-tree.h"
-
-struct json_tree {
- pool_t pool;
- struct json_tree_node *root, *cur, *cur_child;
-};
-
-struct json_tree *
-json_tree_init_type(enum json_type container)
-{
- struct json_tree *tree;
- pool_t pool;
-
- pool = pool_alloconly_create("json tree", 1024);
- tree = p_new(pool, struct json_tree, 1);
- tree->pool = pool;
- tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1);
- tree->cur->value_type = container == JSON_TYPE_ARRAY ? container : JSON_TYPE_OBJECT;
- return tree;
-}
-
-void json_tree_deinit(struct json_tree **_tree)
-{
- struct json_tree *tree = *_tree;
-
- *_tree = NULL;
- pool_unref(&tree->pool);
-}
-
-static void
-json_tree_append_child(struct json_tree *tree, enum json_type type,
- const char *value)
-{
- struct json_tree_node *node;
-
- node = p_new(tree->pool, struct json_tree_node, 1);
- node->parent = tree->cur;
- node->value_type = type;
- node->value.str = p_strdup(tree->pool, value);
-
- if (tree->cur_child == NULL)
- tree->cur->value.child = node;
- else
- tree->cur_child->next = node;
- tree->cur_child = node;
-}
-
-static void
-json_tree_set_cur(struct json_tree *tree, struct json_tree_node *node)
-{
- tree->cur = node;
- tree->cur_child = tree->cur->value.child;
- if (tree->cur_child != NULL) {
- while (tree->cur_child->next != NULL)
- tree->cur_child = tree->cur_child->next;
- }
-}
-
-static int
-json_tree_append_value(struct json_tree *tree, enum json_type type,
- const char *value)
-{
- switch (tree->cur->value_type) {
- case JSON_TYPE_OBJECT_KEY:
- /* "key": value - we already added the node and set its key,
- so now just set the value */
- tree->cur->value_type = type;
- tree->cur->value.str = p_strdup(tree->pool, value);
- json_tree_set_cur(tree, tree->cur->parent);
- break;
- case JSON_TYPE_ARRAY:
- /* element in array - add a new node */
- json_tree_append_child(tree, type, value);
- break;
- default:
- return -1;
- }
- return 0;
-}
-
-int json_tree_append(struct json_tree *tree, enum json_type type,
- const char *value)
-{
- switch (type) {
- case JSON_TYPE_OBJECT_KEY:
- if (tree->cur->value_type != JSON_TYPE_OBJECT)
- return -1;
- json_tree_append_child(tree, type, NULL);
- json_tree_set_cur(tree, tree->cur_child);
- tree->cur->key = p_strdup(tree->pool, value);
- break;
- case JSON_TYPE_ARRAY:
- if (json_tree_append_value(tree, type, NULL) < 0)
- return -1;
- json_tree_set_cur(tree, tree->cur_child);
- break;
- case JSON_TYPE_OBJECT:
- if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY)
- tree->cur->value_type = JSON_TYPE_OBJECT;
- else if (tree->cur->value_type == JSON_TYPE_ARRAY) {
- json_tree_append_child(tree, type, NULL);
- json_tree_set_cur(tree, tree->cur_child);
- } else {
- return -1;
- }
- break;
- case JSON_TYPE_OBJECT_END:
- if (tree->cur->parent == NULL ||
- tree->cur->value_type != JSON_TYPE_OBJECT)
- return -1;
- json_tree_set_cur(tree, tree->cur->parent);
- break;
- case JSON_TYPE_ARRAY_END:
- if (tree->cur->parent == NULL ||
- tree->cur->value_type != JSON_TYPE_ARRAY)
- return -1;
- json_tree_set_cur(tree, tree->cur->parent);
- break;
- case JSON_TYPE_STRING:
- case JSON_TYPE_NUMBER:
- case JSON_TYPE_TRUE:
- case JSON_TYPE_FALSE:
- case JSON_TYPE_NULL:
- if (json_tree_append_value(tree, type, value) < 0)
- return -1;
- break;
- }
- return 0;
-}
-
-const struct json_tree_node *
-json_tree_root(const struct json_tree *tree)
-{
- return tree->root;
-}
-
-const struct json_tree_node *
-json_tree_find_key(const struct json_tree_node *node, const char *key)
-{
- i_assert(node->value_type == JSON_TYPE_OBJECT);
-
- node = json_tree_get_child(node);
- for (; node != NULL; node = node->next) {
- if (node->key != NULL && strcmp(node->key, key) == 0)
- return node;
- }
- return NULL;
-}
-
-const struct json_tree_node *
-json_tree_find_child_with(const struct json_tree_node *node,
- const char *key, const char *value)
-{
- const struct json_tree_node *child;
-
- i_assert(node->value_type == JSON_TYPE_OBJECT ||
- node->value_type == JSON_TYPE_ARRAY);
-
- for (node = json_tree_get_child(node); node != NULL; node = node->next) {
- if (node->value_type != JSON_TYPE_OBJECT)
- continue;
-
- child = json_tree_find_key(node, key);
- if (child != NULL &&
- json_tree_get_value_str(child) != NULL &&
- strcmp(json_tree_get_value_str(child), value) == 0)
- return node;
- }
- return NULL;
-}
+++ /dev/null
-#ifndef JSON_TREE_H
-#define JSON_TREE_H
-
-#include "json-parser.h"
-
-/* Direct access to this structure is not encouraged, use the inline
- function accessors where possible, so that the implementation
- details can remain fluid, or, even better, hidden. */
-struct json_tree_node {
- /* object key, or NULL if we're in a list */
- const char *key;
- struct json_tree_node *parent, *next;
-
- enum json_type value_type;
- struct {
- /* for JSON_TYPE_OBJECT and JSON_TYPE_ARRAY */
- struct json_tree_node *child;
- /* for other types */
- const char *str;
- } value;
-};
-static inline ATTR_PURE const struct json_tree_node *json_tree_get_child(const struct json_tree_node *node)
-{
- return node->value.child;
-}
-static inline ATTR_PURE const char *json_tree_get_value_str(const struct json_tree_node *node)
-{
- return node->value.str;
-}
-
-/* You can build a list or an object, nothing else. */
-struct json_tree *json_tree_init_type(enum json_type container);
-static inline struct json_tree *json_tree_init(void)
-{
- return json_tree_init_type(JSON_TYPE_OBJECT);
-}
-static inline struct json_tree *json_tree_init_array(void)
-{
- return json_tree_init_type(JSON_TYPE_ARRAY);
-}
-
-void json_tree_deinit(struct json_tree **tree);
-
-/* Append data to a tree. The type/value should normally come from json-parser.
- Returns 0 on success, -1 if the input was invalid (which should never happen
- if it's coming from json-parser). */
-int json_tree_append(struct json_tree *tree, enum json_type type,
- const char *value);
-
-/* Return the root node. */
-const struct json_tree_node *
-json_tree_root(const struct json_tree *tree);
-/* Find a node with the specified key from an OBJECT node */
-const struct json_tree_node *
-json_tree_find_key(const struct json_tree_node *node, const char *key);
-/* Find an object node (from an array), which contains the specified key=value
- in its values. */
-const struct json_tree_node *
-json_tree_find_child_with(const struct json_tree_node *node,
- const char *key, const char *value);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
-
-#include "test-lib.h"
-#include "str.h"
-#include "istream-private.h"
-#include "istream-jsonstr.h"
-
-static const struct {
- const char *input;
- const char *output;
- int stream_errno;
-} tests[] = {
- { "foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"",
- "foo\\\"\b\f\n\r\t\001\xEF\xBF\xBF", 0 },
- { "\\ud801\\udc37\"", "\xf0\x90\x90\xb7", 0 }, /* valid codepoint */
- { "\"", "", 0 },
- { "foo\\?\"", "foo", EINVAL },
- { "foo\\?\"", "foo", EINVAL },
- { "", "", EPIPE },
- { "\\\"", "\"", EPIPE },
- { "foo", "foo", EPIPE },
- { "\\ud801", "", EPIPE }, /* high surrogate alone */
- { "\\udced\\udc37\"", "", EINVAL }, /* low surrogate before high */
- { "\\ud8011\\udc37\"", "", EINVAL }, /* has extra 1 in middle */
- { "hello \\udc37\"", "hello ", EINVAL }, /* low surrogate before high with valid prefix*/
- { "hello \\ud801", "hello ", EPIPE }, /* high surrogate alone with valid prefix */
- { "\\uabcg", "", EINVAL }, /* invalid hex value */
-};
-
-static void
-run_test_buffer(const char *json_input, const char *output, int stream_errno,
- unsigned int skip_count)
-{
- size_t json_input_len = strlen(json_input);
- struct istream *input_data, *input;
- const unsigned char *data;
- size_t i, size;
- ssize_t ret = 0;
-
- input_data = test_istream_create_data(json_input, json_input_len);
- test_istream_set_allow_eof(input_data, FALSE);
- input = i_stream_create_jsonstr(input_data);
-
- for (i = 1; i < json_input_len;) {
- test_istream_set_size(input_data, i);
- while ((ret = i_stream_read(input)) > 0) ;
- if (ret == -1 && stream_errno != 0)
- break;
- test_assert_idx(ret == 0, i);
- if (i + skip_count < json_input_len)
- i += skip_count;
- else
- i++;
- }
- test_istream_set_allow_eof(input_data, TRUE);
- test_istream_set_size(input_data, json_input_len);
- ret = i_stream_read(input);
- while (ret > 0 && stream_errno != 0)
- ret = i_stream_read(input);
- test_assert(ret == -1);
- test_assert(input->stream_errno == stream_errno);
-
- if (stream_errno == 0) {
- data = i_stream_get_data(input, &size);
- test_assert(size == strlen(output));
- if (size > 0)
- test_assert(memcmp(data, output, size) == 0);
- }
- i_stream_unref(&input);
- i_stream_unref(&input_data);
-}
-
-static void
-run_test(const char *json_input, const char *output, int stream_errno)
-{
- for (unsigned int i = 1; i <= 5; i++)
- run_test_buffer(json_input, output, stream_errno, i);
-}
-
-static void test_istream_jsonstr_autoretry(void)
-{
- const char *json_input = "\\u0001\"";
- const size_t json_input_len = strlen(json_input);
- struct istream *input_data, *input;
-
- test_begin("istream-jsonstr autoretry");
- input_data = test_istream_create_data(json_input, json_input_len);
- input = i_stream_create_jsonstr(input_data);
-
- test_istream_set_size(input_data, 2);
- test_assert(i_stream_read(input_data) == 2);
- test_istream_set_size(input_data, json_input_len);
- test_assert(i_stream_read(input) == 1);
- test_assert(i_stream_read(input) == -1);
-
- i_stream_unref(&input);
- i_stream_unref(&input_data);
- test_end();
-}
-
-static void test_istream_jsonstr_partial(void)
-{
- size_t len = 0;
- const char *json_input = "hello\\u0060x\"";
- const char *output = "hello`x";
- const size_t json_input_len = strlen(json_input);
- struct istream *input_data, *input;
-
- test_begin("istream-jsonstr partial");
-
- input_data = test_istream_create_data(json_input, json_input_len);
- input = i_stream_create_jsonstr(input_data);
- test_istream_set_size(input_data, 9);
- test_assert(i_stream_read(input) == 5);
- test_istream_set_size(input_data, json_input_len);
- test_assert(i_stream_read(input) == 2);
- test_assert(i_stream_read(input) == -1);
-
- const unsigned char *data = i_stream_get_data(input, &len);
- test_assert_cmp(len, ==, strlen(output));
- test_assert_strcmp((const char*) data, output);
-
- i_stream_unref(&input);
- i_stream_unref(&input_data);
-
- test_end();
-}
-
-void test_istream_jsonstr(void)
-{
- unsigned int i;
-
- for (i = 0; i < N_ELEMENTS(tests); i++) {
- test_begin(t_strdup_printf("istream-jsonstr %u", i+1));
- run_test(tests[i].input, tests[i].output, tests[i].stream_errno);
- test_end();
- }
- test_istream_jsonstr_autoretry();
- test_istream_jsonstr_partial();
-}
+++ /dev/null
-/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
-
-#include "test-lib.h"
-#include "str.h"
-#include "istream-private.h"
-#include "json-parser.h"
-
-#define TYPE_SKIP 100
-#define TYPE_STREAM 101
-
-static const char json_input[] =
- "{\n"
- "\t\"key\"\t:\t\t\"string\","
- " \"key2\" : 1234, \n"
- "\"key3\":true,"
- "\"key4\":false,"
- "\"skip1\": \"jsifjaisfjiasji\","
- "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]},"
- "\"key5\":null,"
- "\"key6\": {},"
- "\"key7\": {"
- " \"sub1\":\"value\""
- "},"
- "\"key8\": {"
- " \"sub2\":-12.456,\n"
- " \"sub3\":12.456e9,\n"
- " \"sub4\":0.456e-789"
- "},"
- "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\u10ff\","
- "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\u10ff\","
- "\"key11\": [],"
- "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}],"
- "\"key13\": \"\\ud801\\udc37\","
- "\"key14\": \"\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85\","
- "\"key15\": \"\\u10000\""
- "}\n";
-
-static const struct {
- enum json_type type;
- const char *value;
-} json_output[] = {
- { JSON_TYPE_OBJECT_KEY, "key" },
- { JSON_TYPE_STRING, "string" },
- { JSON_TYPE_OBJECT_KEY, "key2" },
- { JSON_TYPE_NUMBER, "1234" },
- { JSON_TYPE_OBJECT_KEY, "key3" },
- { JSON_TYPE_TRUE, "true" },
- { JSON_TYPE_OBJECT_KEY, "key4" },
- { JSON_TYPE_FALSE, "false" },
- { JSON_TYPE_OBJECT_KEY, "skip1" },
- { TYPE_SKIP, NULL },
- { JSON_TYPE_OBJECT_KEY, "skip2" },
- { TYPE_SKIP, NULL },
- { JSON_TYPE_OBJECT_KEY, "key5" },
- { JSON_TYPE_NULL, NULL },
- { JSON_TYPE_OBJECT_KEY, "key6" },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_OBJECT_KEY, "key7" },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "sub1" },
- { JSON_TYPE_STRING, "value" },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_OBJECT_KEY, "key8" },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "sub2" },
- { JSON_TYPE_NUMBER, "-12.456" },
- { JSON_TYPE_OBJECT_KEY, "sub3" },
- { JSON_TYPE_NUMBER, "12.456e9" },
- { JSON_TYPE_OBJECT_KEY, "sub4" },
- { JSON_TYPE_NUMBER, "0.456e-789" },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_OBJECT_KEY, "key9" },
- { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xe1\x83\xbf" },
- { JSON_TYPE_OBJECT_KEY, "key10" },
- { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xe1\x83\xbf" },
- { JSON_TYPE_OBJECT_KEY, "key11" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT_KEY, "key12" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_STRING, "foo" },
- { JSON_TYPE_NUMBER, "5.24" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_TRUE, "true" },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "aobj" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT_KEY, "key13" },
- { JSON_TYPE_STRING, "\xf0\x90\x90\xb7" },
- { JSON_TYPE_OBJECT_KEY, "key14" },
- { JSON_TYPE_STRING, "\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85" },
- { JSON_TYPE_OBJECT_KEY, "key15" },
- { JSON_TYPE_STRING, "\xe1\x80\x80""0" },
-};
-
-static int
-stream_read_value(struct istream **input, const char **value_r)
-{
- const unsigned char *data;
- size_t size;
- ssize_t ret;
-
- while ((ret = i_stream_read(*input)) > 0) ;
- if (ret == 0)
- return 0;
- i_assert(ret == -1);
- if ((*input)->stream_errno != 0)
- return -1;
-
- data = i_stream_get_data(*input, &size);
- *value_r = t_strndup(data, size);
- i_stream_unref(input);
- return 1;
-}
-
-static void test_json_parser_success(bool full_size)
-{
- struct json_parser *parser;
- struct istream *input, *jsoninput = NULL;
- enum json_type type;
- const char *value, *error;
- unsigned int i, pos, json_input_len = strlen(json_input);
- int ret = 0;
-
- test_begin(full_size ? "json parser" : "json parser (nonblocking)");
- input = test_istream_create_data(json_input, json_input_len);
- test_istream_set_allow_eof(input, FALSE);
- parser = json_parser_init(input);
-
- i = full_size ? json_input_len : 0;
- for (pos = 0; i <= json_input_len; i++) {
- test_istream_set_size(input, i);
-
- for (;;) {
- value = NULL;
- if (pos < N_ELEMENTS(json_output) &&
- json_output[pos].type == (enum json_type)TYPE_SKIP) {
- json_parse_skip_next(parser);
- pos++;
- continue;
- } else if (pos == N_ELEMENTS(json_output) ||
- json_output[pos].type != (enum json_type)TYPE_STREAM) {
- ret = json_parse_next(parser, &type, &value);
- } else {
- ret = jsoninput != NULL ? 1 :
- json_parse_next_stream(parser, &jsoninput);
- if (ret > 0 && jsoninput != NULL)
- ret = stream_read_value(&jsoninput, &value);
- type = TYPE_STREAM;
- }
- if (ret <= 0)
- break;
-
- i_assert(pos < N_ELEMENTS(json_output));
- test_assert_idx(json_output[pos].type == type, pos);
- test_assert_idx(null_strcmp(json_output[pos].value, value) == 0, pos);
-
- pos++;
- }
- test_assert_idx(ret == 0, pos);
- }
- test_assert(pos == N_ELEMENTS(json_output));
- test_istream_set_allow_eof(input, TRUE);
- test_assert(json_parse_next(parser, &type, &value) == -1);
-
- i_stream_unref(&input);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- test_end();
-}
-
-static void test_json_parser_skip_array(void)
-{
- static const char *test_input =
- "[ 1, {\"foo\": 1 }, 2, \"bar\", 3, 1.234, 4, [], 5, [[]], 6, true ]";
- struct json_parser *parser;
- struct istream *input;
- enum json_type type;
- const char *value, *error;
- int i;
-
- test_begin("json parser skip array");
-
- input = test_istream_create_data(test_input, strlen(test_input));
- parser = json_parser_init_flags(input, JSON_PARSER_NO_ROOT_OBJECT);
- test_assert(json_parse_next(parser, &type, &value) > 0 &&
- type == JSON_TYPE_ARRAY);
- for (i = 1; i <= 6; i++) {
- test_assert(json_parse_next(parser, &type, &value) > 0 &&
- type == JSON_TYPE_NUMBER && atoi(value) == i);
- json_parse_skip_next(parser);
- }
- test_assert(json_parse_next(parser, &type, &value) > 0 &&
- type == JSON_TYPE_ARRAY_END);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- i_stream_unref(&input);
- test_end();
-}
-
-static void test_json_parser_skip_object_fields(void)
-{
- static const char *test_input =
- "{\"access_token\":\"9a2dea3c-f8be-4271-b9c8-5b37da4f2f7e\","
- "\"grant_type\":\"authorization_code\","
- "\"openid\":\"\","
- "\"scope\":[\"openid\",\"profile\",\"email\"],"
- "\"profile\":\"\","
- "\"realm\":\"/employees\","
- "\"token_type\":\"Bearer\","
- "\"expires_in\":2377,"
- "\"client_i\\u0064\":\"mosaic\\u0064\","
- "\"email\":\"\","
- "\"extensions\":"
- "{\"algorithm\":\"cuttlefish\","
- "\"tentacles\":8"
- "}"
- "}";
- static const char *const keys[] = {
- "access_token", "grant_type", "openid", "scope", "profile",
- "realm", "token_type", "expires_in", "client_id", "email",
- "extensions"
- };
- static const unsigned int keys_count = N_ELEMENTS(keys);
- struct json_parser *parser;
- struct istream *input;
- enum json_type type;
- const char *value, *error;
- unsigned int i;
- size_t pos;
- int ret;
-
- test_begin("json parser skip object fields (by key)");
- input = test_istream_create_data(test_input, strlen(test_input));
- parser = json_parser_init(input);
- for (i = 0; i < keys_count; i++) {
- ret = json_parse_next(parser, &type, &value);
- if (ret < 0)
- break;
- test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY);
- test_assert(strcmp(value, keys[i]) == 0);
- json_parse_skip_next(parser);
- }
- test_assert(i == keys_count);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- i_stream_unref(&input);
-
- i = 0;
- input = test_istream_create_data(test_input, strlen(test_input));
- parser = json_parser_init(input);
- for (pos = 0; pos <= strlen(test_input)*2; pos++) {
- test_istream_set_size(input, pos/2);
- ret = json_parse_next(parser, &type, &value);
- if (ret == 0)
- continue;
- if (ret < 0)
- break;
- i_assert(i < keys_count);
- test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY);
- test_assert(strcmp(value, keys[i]) == 0);
- json_parse_skip_next(parser);
- i++;
- }
- test_assert(i == keys_count);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- i_stream_unref(&input);
- test_end();
-
- test_begin("json parser skip object fields (by value type)");
- input = test_istream_create_data(test_input, strlen(test_input));
- parser = json_parser_init(input);
- for (i = 0; i < keys_count; i++) {
- ret = json_parse_next(parser, &type, &value);
- if (ret < 0)
- break;
- test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY);
- test_assert(strcmp(value, keys[i]) == 0);
- ret = json_parse_next(parser, &type, &value);
- test_assert(ret > 0 && type != JSON_TYPE_OBJECT_KEY);
- json_parse_skip(parser);
- }
- test_assert(i == keys_count);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- i_stream_unref(&input);
-
- i = 0;
- input = test_istream_create_data(test_input, strlen(test_input));
- parser = json_parser_init(input);
- for (pos = 0; pos <= strlen(test_input)*2; pos++) {
- test_istream_set_size(input, pos/2);
- ret = json_parse_next(parser, &type, &value);
- if (ret < 0)
- break;
- if (ret == 0)
- continue;
- test_assert(ret > 0);
- if (type == JSON_TYPE_OBJECT_KEY) {
- i_assert(i < keys_count);
- test_assert(strcmp(value, keys[i]) == 0);
- i++;
- } else {
- json_parse_skip(parser);
- }
- }
- test_assert(i == keys_count);
- test_assert(json_parser_deinit(&parser, &error) == 0);
- i_stream_unref(&input);
-
- test_end();
-}
-
-static int
-test_json_parse_input(const void *test_input, size_t test_input_size,
- enum json_parser_flags flags)
-{
- struct json_parser *parser;
- struct istream *input;
- enum json_type type;
- const char *value, *error;
- int ret = 0;
-
- input = test_istream_create_data(test_input, test_input_size);
- parser = json_parser_init_flags(input, flags);
- while (json_parse_next(parser, &type, &value) > 0)
- ret++;
- if (json_parser_deinit(&parser, &error) < 0)
- ret = -1;
- i_stream_unref(&input);
- return ret;
-}
-
-static void test_json_parser_primitive_values(void)
-{
- static const struct {
- const char *str;
- int ret;
- } test_inputs[] = {
- { "\"hello\"", 1 },
- { "null", 1 },
- { "1234", 1 },
- { "1234.1234", 1 },
- { "{}", 2 },
- { "[]", 2 },
- { "true", 1 },
- { "false", 1 }
- };
- unsigned int i;
-
- test_begin("json_parser (primitives)");
- for (i = 0; i < N_ELEMENTS(test_inputs); i++)
- test_assert_idx(test_json_parse_input(test_inputs[i].str,
- strlen(test_inputs[i].str),
- JSON_PARSER_NO_ROOT_OBJECT) == test_inputs[i].ret, i);
- test_end();
-}
-
-static void test_json_parser_errors(void)
-{
- static const char *test_inputs[] = {
- "{",
- "{:}",
- "{\"foo\":}",
- "{\"foo\" []}",
- "{\"foo\": [1}",
- "{\"foo\": [1,]}",
- "{\"foo\": [1,]}",
- "{\"foo\": 1,}",
- "{\"foo\": 1.}}",
- "{\"foo\": 1},{}",
- "{\"foo\": \"\\ud808\"}",
- "{\"foo\": \"\\udfff\"}",
- "{\"foo\": \"\\uyyyy\"}",
- "{\"a\":\"",
- "{\"a\":nul",
- "{\"a\":fals",
- "{\"a\":tru",
- };
- unsigned int i;
-
- test_begin("json parser error handling");
- for (i = 0; i < N_ELEMENTS(test_inputs); i++)
- test_assert_idx(test_json_parse_input(test_inputs[i],
- strlen(test_inputs[i]),
- 0) < 0, i);
- test_end();
-}
-
-static void test_json_parser_nuls_in_string(void)
-{
- static const unsigned char test_input[] =
- { '{', '"', 'k', '"', ':', '"', '\0', '"', '}' };
- static const unsigned char test_input2[] =
- { '{', '"', 'k', '"', ':', '"', '\\', '\0', '"', '}' };
- static const unsigned char test_input3[] =
- { '{', '"', 'k', '"', ':', '"', '\\', 'u', '0', '0', '0', '0', '"', '}' };
-
- test_begin("json parser nuls in string");
- test_assert(test_json_parse_input(test_input, sizeof(test_input), 0) < 0);
- test_assert(test_json_parse_input(test_input2, sizeof(test_input2), 0) < 0);
- test_assert(test_json_parse_input(test_input3, sizeof(test_input3), 0) < 0);
- test_end();
-}
-
-static void test_json_append_escaped(void)
-{
- string_t *str = t_str_new(32);
-
- test_begin("json_append_escaped()");
- json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4\xf0\x90\x90\xb7\xe2\x80\xa8\xe2\x80\xa9\xff");
- test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\xC3\xA4\xf0\x90\x90\xb7\\u2028\\u2029" UNICODE_REPLACEMENT_CHAR_UTF8) == 0);
- test_end();
-}
-
-static void test_json_append_escaped_data(void)
-{
- static const unsigned char test_input[] =
- "\b\f\r\n\t\"\\\000\001\002-\xC3\xA4\xf0\x90\x90\xb7\xe2\x80\xa8\xe2\x80\xa9\xff";
- string_t *str = t_str_new(32);
-
- test_begin("json_append_escaped_data()");
- json_append_escaped_data(str, test_input, sizeof(test_input)-1);
- test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0000\\u0001\\u0002-\xC3\xA4\xf0\x90\x90\xb7\\u2028\\u2029" UNICODE_REPLACEMENT_CHAR_UTF8) == 0);
- test_end();
-}
-
-void test_json_parser(void)
-{
- test_json_parser_success(TRUE);
- test_json_parser_success(FALSE);
- test_json_parser_skip_array();
- test_json_parser_skip_object_fields();
- test_json_parser_primitive_values();
- test_json_parser_errors();
- test_json_parser_nuls_in_string();
- test_json_append_escaped();
- test_json_append_escaped_data();
-}
+++ /dev/null
-/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
-
-#include "test-lib.h"
-#include "json-tree.h"
-
-struct {
- enum json_type type;
- const char *value;
-} test_input[] = {
- { JSON_TYPE_OBJECT_KEY, "key-str" },
- { JSON_TYPE_STRING, "string" },
- { JSON_TYPE_OBJECT_KEY, "key-num" },
- { JSON_TYPE_NUMBER, "1234" },
- { JSON_TYPE_OBJECT_KEY, "key-true" },
- { JSON_TYPE_TRUE, "true" },
- { JSON_TYPE_OBJECT_KEY, "key-false" },
- { JSON_TYPE_FALSE, "false" },
- { JSON_TYPE_OBJECT_KEY, "key-null" },
- { JSON_TYPE_NULL, NULL },
-
- { JSON_TYPE_OBJECT_KEY, "key-obj-empty" },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_END, NULL },
-
- { JSON_TYPE_OBJECT_KEY, "key-obj" },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "sub" },
- { JSON_TYPE_STRING, "value" },
- { JSON_TYPE_OBJECT_END, NULL },
-
- { JSON_TYPE_OBJECT_KEY, "key-arr-empty" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_ARRAY_END, NULL },
-
- { JSON_TYPE_OBJECT_KEY, "key-arr" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_STRING, "foo" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_TRUE, "true" },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "aobj" },
- { JSON_TYPE_ARRAY, NULL },
- { JSON_TYPE_ARRAY_END, NULL },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "aobj-key" },
- { JSON_TYPE_STRING, "value1" },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_OBJECT, NULL },
- { JSON_TYPE_OBJECT_KEY, "aobj-key" },
- { JSON_TYPE_STRING, "value2" },
- { JSON_TYPE_OBJECT_END, NULL },
- { JSON_TYPE_ARRAY_END, NULL }
-};
-
-void test_json_tree(void)
-{
- struct json_tree *tree;
- const struct json_tree_node *root, *node, *node1, *node2;
- unsigned int i;
-
- test_begin("json tree");
- tree = json_tree_init();
- for (i = 0; i < N_ELEMENTS(test_input); i++) {
- test_assert_idx(json_tree_append(tree, test_input[i].type,
- test_input[i].value) == 0, i);
- }
-
- root = json_tree_root(tree);
- i_assert(root != NULL);
- test_assert(root->value_type == JSON_TYPE_OBJECT);
- i_assert(root != NULL);
-
- for (i = 0; i < 10+2; i += 2) {
- node = json_tree_find_key(root, test_input[i].value);
- test_assert(node != NULL &&
- node->value_type == test_input[i+1].type &&
- null_strcmp(json_tree_get_value_str(node), test_input[i+1].value) == 0);
- }
-
- node = json_tree_find_key(root, "key-obj");
- test_assert(node != NULL);
-
- node = json_tree_find_key(root, "key-arr-empty");
- test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY &&
- json_tree_get_child(node) == NULL);
-
- node = json_tree_find_key(root, "key-arr");
- test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY);
- node = json_tree_get_child(node);
- test_assert(node != NULL && node->value_type == JSON_TYPE_STRING &&
- strcmp(json_tree_get_value_str(node), "foo") == 0);
- node = node->next;
- test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY &&
- json_tree_get_child(node) != NULL &&
- json_tree_get_child(node)->next == NULL &&
- json_tree_get_child(node)->value_type == JSON_TYPE_TRUE);
- node = node->next;
- test_assert(node != NULL && node->value_type == JSON_TYPE_OBJECT &&
- json_tree_get_child(node) != NULL &&
- json_tree_get_child(node)->next == NULL &&
- json_tree_get_child(node)->value_type == JSON_TYPE_ARRAY &&
- json_tree_get_child(json_tree_get_child(node)) == NULL);
-
- node1 = json_tree_find_child_with(node->parent, "aobj-key", "value1");
- node2 = json_tree_find_child_with(node->parent, "aobj-key", "value2");
- test_assert(node1 != NULL && node2 != NULL && node1 != node2);
- test_assert(json_tree_find_child_with(node->parent, "aobj-key", "value3") == NULL);
-
- json_tree_deinit(&tree);
- test_end();
-}
TEST(test_istream_concat)
TEST(test_istream_crlf)
TEST(test_istream_failure_at)
-TEST(test_istream_jsonstr)
TEST(test_istream_multiplex)
TEST(test_istream_noop)
TEST(test_istream_seekable)
TEST(test_istream_tee)
TEST(test_istream_try)
TEST(test_istream_unix)
-TEST(test_json_parser)
-TEST(test_json_tree)
TEST(test_lib_event)
TEST(test_lib_signals)
TEST(test_llist)