json-parser.c revision a18503d5dc0751a1f9785e48438a219d95c0b9c2
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen uoff_t cur_highwater = parser->input->v_offset +
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen if (parser->highwater_offset == cur_highwater) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* call it once more to finish any pending number */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (ret <= 0) {
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen i_assert(parser->highwater_offset < cur_highwater);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_skip(parser->input, parser->data - parser->start);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we skipped over some data and there's still data left.
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen no need to read() the next time. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->highwater_offset = parser->input->v_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct json_parser *json_parser_init(struct istream *input)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct json_parser *json_parser_init_flags(struct istream *input,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parser_deinit(struct json_parser **_parser, const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* actual parser error */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (parser->input->stream_errno != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool json_parse_whitespace(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (; parser->data != parser->end; parser->data++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_skip_string(struct json_parser *parser)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen for (; parser->data != parser->end; parser->data++) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenstatic int json_parse_string(struct json_parser *parser, bool allow_skip,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const char **value_r)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen for (; parser->data != parser->end; parser->data++) {
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen /* wait for more data */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data < '0' || *parser->data > '9')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_int(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data != parser->end && *parser->data == '.') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen /* everything matches so far, but we need more data */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_denest(struct json_parser *parser)
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen nested_states = array_get(&parser->nesting, &count);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* closing root */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we want to return the ending "]" or "}" to caller */
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen /* closing a nested object */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ?
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_parse_close_object(struct json_parser *parser, enum json_type *type_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_parse_close_array(struct json_parser *parser, enum json_type *type_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void json_parser_object_open(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **value_r)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen parser->error = "Object doesn't begin with '{'";
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen } else if ((ret = json_parse_number(parser, value_r)) >= 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "true")) >= 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "false")) >= 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "null")) >= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping && *type_r == JSON_TYPE_STRING) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* a large string that we want to skip over. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return json_parse_close_object(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* fall through */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (json_parse_string(parser, FALSE, value_r) <= 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected string as object key";
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* we skipped over the previous value */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return json_parse_close_object(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected ',' or '}' after object value";
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen return json_parse_close_array(parser, type_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we skipped over the previous value */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return json_parse_close_array(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected ',' or '}' after array value";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ?
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parse_next(struct json_parser *parser, enum json_type *type_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **value_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* parsing probably failed because there wasn't enough input.
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen reset the error and try reading more. */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->highwater_offset = parser->input->v_offset +
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid json_parse_skip_next(struct json_parser *parser)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void json_strinput_destroyed(struct json_parser *parser)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenjson_try_parse_stream_start(struct json_parser *parser,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->state == JSON_STATE_OBJECT_COLON) {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen parser->strinput = i_stream_create_jsonstr(parser->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_add_destroy_callback(parser->strinput,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parse_next_stream(struct json_parser *parser,
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (json_try_parse_stream_start(parser, input_r) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* parsing probably failed because there wasn't enough input.
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen reset the error and try reading more. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->highwater_offset = parser->input->v_offset +
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstatic void json_append_escaped_char(string_t *dest, unsigned char src)
for (i = 0; i < size; i++)