json-parser.c revision 32bd32dcc845cd0c00d5617aea1ffbe45522b413
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "lib.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "array.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "str.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "istream.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "hex-dec.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "unichar.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "istream-jsonstr.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "json-parser.h"
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenenum json_state {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ROOT = 0,
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen JSON_STATE_OBJECT_OPEN,
25480af2e21cf136e461ec802177f52b43154485Timo Sirainen JSON_STATE_OBJECT_KEY,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_OBJECT_COLON,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_OBJECT_VALUE,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_OBJECT_SKIP_STRING,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_OBJECT_NEXT,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ARRAY_OPEN,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ARRAY_VALUE,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ARRAY_SKIP_STRING,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ARRAY_NEXT,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_ARRAY_NEXT_SKIP,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_VALUE,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen JSON_STATE_DONE
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen};
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstruct json_parser {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen struct istream *input;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen uoff_t highwater_offset;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen enum json_parser_flags flags;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen const unsigned char *start, *end, *data;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen const char *error;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen string_t *value;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen struct istream *strinput;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen enum json_state state;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen ARRAY(enum json_state) nesting;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen unsigned int nested_skip_count;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen bool skipping;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen bool seen_eof;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen};
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen uoff_t cur_highwater = parser->input->v_offset +
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen i_stream_get_data_size(parser->input);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen size_t size;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen ssize_t ret;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen if (parser->error != NULL)
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen return -1;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen if (parser->highwater_offset == cur_highwater) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen ret = i_stream_read(parser->input);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (ret == -2) {
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen parser->error = "Token too large";
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen return -1;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen }
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen if (ret < 0 && !parser->seen_eof &&
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen i_stream_get_data_size(parser->input) > 0 &&
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen parser->input->stream_errno == 0) {
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen /* call it once more to finish any pending number */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->seen_eof = TRUE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen } else if (ret <= 0) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return ret;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen } else {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen cur_highwater = parser->input->v_offset +
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen i_stream_get_data_size(parser->input);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen i_assert(parser->highwater_offset < cur_highwater);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen parser->highwater_offset = cur_highwater;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->end = parser->start + size;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen i_assert(size > 0);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return 1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen size_t size;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen if (parser->data == parser->start)
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen return;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen i_stream_skip(parser->input, parser->data - parser->start);
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen parser->end = parser->start + size;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen if (size > 0) {
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen /* we skipped over some data and there's still data left.
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen no need to read() the next time. */
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen parser->highwater_offset = 0;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen } else {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->highwater_offset = parser->input->v_offset;
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainenstruct json_parser *json_parser_init(struct istream *input)
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen{
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen return json_parser_init_flags(input, 0);
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen}
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainenstruct json_parser *json_parser_init_flags(struct istream *input,
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen enum json_parser_flags flags)
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen{
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody struct json_parser *parser;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody parser = i_new(struct json_parser, 1);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody parser->input = input;
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen parser->flags = flags;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen parser->value = str_new(default_pool, 128);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen i_array_init(&parser->nesting, 8);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen i_stream_ref(input);
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen parser->state = JSON_STATE_VALUE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return parser;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody}
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmodyint json_parser_deinit(struct json_parser **_parser, const char **error_r)
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody{
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody struct json_parser *parser = *_parser;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody *_parser = NULL;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody if (parser->error != NULL) {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody /* actual parser error */
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody *error_r = parser->error;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody } else if (parser->input->stream_errno != 0) {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody *error_r = t_strdup_printf("read(%s) failed: %s",
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody i_stream_get_name(parser->input),
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody i_stream_get_error(parser->input));
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody } else if (parser->data == parser->end &&
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody !i_stream_have_bytes_left(parser->input) &&
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody parser->state != JSON_STATE_DONE) {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody *error_r = "Missing '}'";
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody } else {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody *error_r = NULL;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody }
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody i_stream_unref(&parser->input);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody array_free(&parser->nesting);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody str_free(&parser->value);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody i_free(parser);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody return *error_r != NULL ? -1 : 0;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody}
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmodystatic bool json_parse_whitespace(struct json_parser *parser)
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody{
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody for (; parser->data != parser->end; parser->data++) {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody switch (*parser->data) {
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody case ' ':
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody case '\t':
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody case '\r':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case '\n':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen break;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen default:
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen json_parser_update_input_pos(parser);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return TRUE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen json_parser_update_input_pos(parser);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return FALSE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen}
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainenstatic int json_skip_string(struct json_parser *parser)
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen{
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen for (; parser->data != parser->end; parser->data++) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (*parser->data == '"') {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen parser->data++;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen json_parser_update_input_pos(parser);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return 1;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (*parser->data == '\\') {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen switch (*++parser->data) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case '"':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case '\\':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case '/':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case 'b':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case 'f':
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen case 'n':
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen case 'r':
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen case 't':
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody break;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody case 'u':
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody if (parser->end - parser->data < 4)
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody return -1;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody parser->data += 3;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody break;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody default:
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody return -1;
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody }
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody }
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody }
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody json_parser_update_input_pos(parser);
ba66ac5557ca97d8a6fe5d524056264a9f92243cPhil Carmody return 0;
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen}
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainenstatic int json_parse_unicode_escape(struct json_parser *parser)
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen{
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen unichar_t chr, hi_surg;
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen parser->data++;
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen if (parser->end - parser->data < 4) {
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen /* wait for more data */
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen parser->data = parser->end;
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen return 0;
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen }
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen chr = hex2dec(parser->data, 4);
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen if (UTF16_VALID_HIGH_SURROGATE(chr)) {
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen /* possible surrogate pair */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen hi_surg = chr;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen chr = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->data += 4;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (parser->data >= parser->end) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen /* wait for more data */
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen parser->data = parser->end;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return 0;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen }
e8ecd8f24ffc612f5d0be10f7931ac619f1eab88Timo Sirainen if ((parser->end - parser->data) < 2) {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen if (parser->data[0] == '\\') {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen /* wait for more data */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->data = parser->end;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* error */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if ((parser->end - parser->data) < 6) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (parser->data[0] == '\\' &&
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen parser->data[1] == 'u') {
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen /* wait for more data */
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen parser->data = parser->end;
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen return 0;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen /* error */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen } else {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen chr = hex2dec(&parser->data[2], 4);
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen }
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen if (parser->data[0] != '\\' || parser->data[1] != 'u' ||
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen !UTF16_VALID_LOW_SURROGATE(chr)) {
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen parser->error =
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen t_strdup_printf("High surrogate 0x%04x seen, "
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen "but not followed by low surrogate",
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen hi_surg);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return -1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
25480af2e21cf136e461ec802177f52b43154485Timo Sirainen chr = uni_join_surrogate(hi_surg, chr);
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen parser->data += 2;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (!uni_is_valid_ucs4(chr)) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->error =
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen t_strdup_printf("Invalid unicode character U+%04x", chr);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return -1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen uni_ucs4_to_utf8_c(chr, parser->value);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->data += 3;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return 1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstatic int json_parse_string(struct json_parser *parser, bool allow_skip,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen const char **value_r)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen int ret;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (*parser->data != '"')
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return -1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen parser->data++;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (parser->skipping && allow_skip) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen *value_r = NULL;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return json_skip_string(parser);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen str_truncate(parser->value, 0);
4c9a72e0988d462df49810984dc93b3fd4a24c23Timo Sirainen for (; parser->data != parser->end; parser->data++) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (*parser->data == '"') {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen parser->data++;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen *value_r = str_c(parser->value);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return 1;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (*parser->data != '\\')
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen str_append_c(parser->value, *parser->data);
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen else {
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (++parser->data == parser->end)
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen return 0;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen switch (*parser->data) {
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen case '"':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen case '\\':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen case '/':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen str_append_c(parser->value, *parser->data);
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen break;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen case 'b':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen str_append_c(parser->value, '\b');
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen break;
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen case 'f':
749a9676d265a517c7a731f5b9336c524a49e6a6Timo Sirainen str_append_c(parser->value, '\f');
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen break;
749a9676d265a517c7a731f5b9336c524a49e6a6Timo Sirainen case 'n':
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen str_append_c(parser->value, '\n');
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen break;
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen case 'r':
5e85a6a1349177c613dea55aabb20d857b8240a5Timo Sirainen str_append_c(parser->value, '\r');
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen break;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen case 't':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen str_append_c(parser->value, '\t');
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen break;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen case 'u':
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen if ((ret=json_parse_unicode_escape(parser)) <= 0)
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen return ret;
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen break;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen default:
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen return -1;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen }
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen }
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen }
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen return 0;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen}
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainenstatic int
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainenjson_parse_digits(struct json_parser *parser)
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen{
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (parser->data == parser->end)
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return 0;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (*parser->data < '0' || *parser->data > '9')
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return -1;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen while (parser->data != parser->end &&
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen *parser->data >= '0' && *parser->data <= '9')
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen str_append_c(parser->value, *parser->data++);
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return 1;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen}
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainenstatic int json_parse_int(struct json_parser *parser)
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen{
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen int ret;
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen if (*parser->data == '-') {
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen str_append_c(parser->value, *parser->data++);
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen if (parser->data == parser->end)
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen return 0;
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen }
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen if (*parser->data == '0')
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen str_append_c(parser->value, *parser->data++);
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen else {
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen return ret;
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen }
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen return 1;
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen}
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen{
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen int ret;
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen str_truncate(parser->value, 0);
199566f5a171b2c43b9a5254634f6bf47b8baca8Timo Sirainen if ((ret = json_parse_int(parser)) <= 0)
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return ret;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (parser->data != parser->end && *parser->data == '.') {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* frac */
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen str_append_c(parser->value, *parser->data++);
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return ret;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen }
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (parser->data != parser->end &&
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* exp */
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen str_append_c(parser->value, *parser->data++);
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (parser->data == parser->end)
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return 0;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen str_append_c(parser->value, *parser->data++);
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return ret;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen }
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return 0;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen *value_r = str_c(parser->value);
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return 1;
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen size_t avail, len = strlen(atom);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen avail = parser->end - parser->data;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (avail < len) {
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (memcmp(parser->data, atom, avail) != 0)
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return -1;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen /* everything matches so far, but we need more data */
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen parser->data += avail;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return 0;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen }
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (memcmp(parser->data, atom, len) != 0)
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return -1;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen parser->data += len;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return 1;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen}
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainenstatic int json_parse_denest(struct json_parser *parser)
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen{
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen const enum json_state *nested_states;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen unsigned count;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen parser->data++;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen json_parser_update_input_pos(parser);
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen nested_states = array_get(&parser->nesting, &count);
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen i_assert(count > 0);
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen if (count == 1) {
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen /* closing root */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen parser->state = JSON_STATE_DONE;
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen return 0;
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen /* we want to return the ending "]" or "}" to caller */
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen return 1;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen }
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen /* closing a nested object */
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ?
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen array_delete(&parser->nesting, count-1, 1);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (parser->nested_skip_count > 0) {
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen parser->nested_skip_count--;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 0;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen }
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 1;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic int
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenjson_parse_close_object(struct json_parser *parser, enum json_type *type_r)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (json_parse_denest(parser) == 0)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 0;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen *type_r = JSON_TYPE_OBJECT_END;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 1;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic int
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenjson_parse_close_array(struct json_parser *parser, enum json_type *type_r)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (json_parse_denest(parser) == 0)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 0;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen *type_r = JSON_TYPE_ARRAY_END;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return 1;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic void json_parser_object_open(struct json_parser *parser)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen parser->data++;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen parser->state = JSON_STATE_OBJECT_OPEN;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen json_parser_update_input_pos(parser);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic int
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen const char **value_r)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen bool skipping = parser->skipping;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen int ret;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (!json_parse_whitespace(parser))
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return -1;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen switch (parser->state) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen case JSON_STATE_ROOT:
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (*parser->data != '{') {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen parser->error = "Object doesn't begin with '{'";
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return -1;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen json_parser_object_open(parser);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return 0;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen case JSON_STATE_OBJECT_VALUE:
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen case JSON_STATE_ARRAY_VALUE:
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen case JSON_STATE_VALUE:
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (*parser->data == '{') {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen json_parser_object_open(parser);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (parser->skipping) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen parser->nested_skip_count++;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return 0;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen *type_r = JSON_TYPE_OBJECT;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return 1;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen } else if (*parser->data == '[') {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen parser->data++;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen parser->state = JSON_STATE_ARRAY_OPEN;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen json_parser_update_input_pos(parser);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (parser->skipping) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen parser->nested_skip_count++;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return 0;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen *type_r = JSON_TYPE_ARRAY;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return 1;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
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;
}
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)
{
unichar_t high,low;
if (chr < 0x80)
json_append_escaped_char(dest, (unsigned char)chr);
else if (chr >= UTF16_SURROGATE_BASE) {
uni_split_surrogate(chr, &high, &low);
str_printfa(dest, "\\u%04x\\u%04x", high, low);
} else {
str_printfa(dest, "\\u%04x", chr);
}
}
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 it was valid unichar, encode + move forward by bytes */
if (bytes > 0) {
json_append_escaped_ucs4(dest, chr);
i += bytes;
/* encode as byte data */
} else {
json_append_escaped_char(dest, src[i++]);
}
}
}