bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen uoff_t cur_highwater = parser->input->v_offset +
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->highwater_offset == cur_highwater) {
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen /* call it once more to finish any pending number */
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen } else if (ret <= 0) {
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen i_assert(parser->highwater_offset < cur_highwater);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_stream_skip(parser->input, parser->data - parser->start);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* we skipped over some data and there's still data left.
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen no need to read() the next time. */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->highwater_offset = parser->input->v_offset;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstruct json_parser *json_parser_init(struct istream *input)
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainenstruct json_parser *json_parser_init_flags(struct istream *input,
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenint json_parser_deinit(struct json_parser **_parser, const char **error_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* actual parser error */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen } else if (parser->input->stream_errno != 0) {
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %s",
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic bool json_parse_whitespace(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen for (; parser->data != parser->end; parser->data++) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenstatic int json_skip_string(struct json_parser *parser)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen for (; parser->data != parser->end; parser->data++) {
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomistatic int json_parse_unicode_escape(struct json_parser *parser)
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi /* wait for more data */
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi /* possible surrogate pair */
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi /* wait for more data */
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi /* wait for more data */
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi /* wait for more data */
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi if (parser->data[0] != '\\' || parser->data[1] != 'u' ||
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi "but not followed by low surrogate",
2f40603562d4cf3fad0bc8ff1f10f652cedd77dfAki Tuomi t_strdup_printf("Invalid unicode character U+%04x", chr);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenstatic int json_parse_string(struct json_parser *parser, bool allow_skip,
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen const char **value_r)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen for (; parser->data != parser->end; parser->data++) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (*parser->data < '0' || *parser->data > '9')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic int json_parse_int(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data != parser->end && *parser->data == '.') {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen /* everything matches so far, but we need more data */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic int json_parse_denest(struct json_parser *parser)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen nested_states = array_get(&parser->nesting, &count);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen /* closing root */
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen /* we want to return the ending "]" or "}" to caller */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen /* closing a nested object */
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ?
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenjson_parse_close_object(struct json_parser *parser, enum json_type *type_r)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenjson_parse_close_array(struct json_parser *parser, enum json_type *type_r)
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainenstatic void json_parser_object_open(struct json_parser *parser)
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char **value_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->error = "Object doesn't begin with '{'";
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen array_append(&parser->nesting, &parser->state, 1);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_number(parser, value_r)) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "true")) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "false")) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "null")) >= 0) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping && *type_r == JSON_TYPE_STRING) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen /* a large string that we want to skip over. */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_object(parser, type_r);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* fall through */
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (json_parse_string(parser, FALSE, value_r) <= 0) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Expected string as object key";
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen /* we skipped over the previous value */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_object(parser, type_r);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Expected ',' or '}' after object value";
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_array(parser, type_r);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen /* we skipped over the previous value */
f784d5bb8edbec88829524135cfa100129f5384dTimo Sirainen /* fall through */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_array(parser, type_r);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->error = "Expected ',' or '}' after array value";
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ?
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenint json_parse_next(struct json_parser *parser, enum json_type *type_r,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char **value_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* parsing probably failed because there wasn't enough input.
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen reset the error and try reading more. */
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen parser->highwater_offset = parser->input->v_offset +
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenvoid json_parse_skip_next(struct json_parser *parser)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstatic void json_strinput_destroyed(struct json_parser *parser)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenjson_try_parse_stream_start(struct json_parser *parser,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (parser->state == JSON_STATE_OBJECT_COLON) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->strinput = i_stream_create_jsonstr(parser->input);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen i_stream_add_destroy_callback(parser->strinput,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenint json_parse_next_stream(struct json_parser *parser,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (json_try_parse_stream_start(parser, input_r) == 0)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen /* parsing probably failed because there wasn't enough input.
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen reset the error and try reading more. */
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen parser->highwater_offset = parser->input->v_offset +
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenstatic void json_append_escaped_char(string_t *dest, unsigned char src)
32bd32dcc845cd0c00d5617aea1ffbe45522b413Aki Tuomivoid json_append_escaped_ucs4(string_t *dest, unichar_t chr)
32bd32dcc845cd0c00d5617aea1ffbe45522b413Aki Tuomi json_append_escaped_char(dest, (unsigned char)chr);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomivoid ostream_escaped_json_format(string_t *dest, unsigned char src)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenvoid json_append_escaped(string_t *dest, const char *src)
32bd32dcc845cd0c00d5617aea1ffbe45522b413Aki Tuomi json_append_escaped_data(dest, (const unsigned char*)src, strlen(src));
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenvoid json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size)
32bd32dcc845cd0c00d5617aea1ffbe45522b413Aki Tuomi for (i = 0; i < size;) {
c3393007354b7ab607449fea0c3d7088193ab208Aki Tuomi /* refuse to add invalid data */