json-parser.c revision f6497ac81e6de57870936d538acccb75ce408fc1
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "lib.h"
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen#include "array.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "str.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "istream.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "hex-dec.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "unichar.h"
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen#include "istream-jsonstr.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "json-parser.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenenum json_state {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen JSON_STATE_ROOT = 0,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen JSON_STATE_OBJECT_OPEN,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen JSON_STATE_OBJECT_KEY,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen JSON_STATE_OBJECT_COLON,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen JSON_STATE_OBJECT_VALUE,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_SKIP_STRING,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_NEXT,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_ARRAY_OPEN,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_ARRAY_VALUE,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_ARRAY_SKIP_STRING,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_ARRAY_NEXT,
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen JSON_STATE_ARRAY_NEXT_SKIP,
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen JSON_STATE_VALUE,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen JSON_STATE_DONE
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen};
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstruct json_parser {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct istream *input;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen uoff_t highwater_offset;
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen enum json_parser_flags flags;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const unsigned char *start, *end, *data;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen const char *error;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen string_t *value;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen struct istream *strinput;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen enum json_state state;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen ARRAY(enum json_state) nesting;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen unsigned int nested_skip_count;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen bool skipping;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen bool seen_eof;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen};
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen{
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen uoff_t cur_highwater = parser->input->v_offset +
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_stream_get_data_size(parser->input);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen size_t size;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen ssize_t ret;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->error != NULL)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->highwater_offset == cur_highwater) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen ret = i_stream_read(parser->input);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (ret == -2) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->error = "Token too large";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen if (ret < 0 && !parser->seen_eof &&
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen i_stream_get_data_size(parser->input) > 0 &&
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen parser->input->stream_errno == 0) {
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen /* call it once more to finish any pending number */
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen parser->seen_eof = TRUE;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen } else if (ret <= 0) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return ret;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen } else {
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen cur_highwater = parser->input->v_offset +
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen i_stream_get_data_size(parser->input);
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen i_assert(parser->highwater_offset < cur_highwater);
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen parser->highwater_offset = cur_highwater;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->end = parser->start + size;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_assert(size > 0);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return 1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen{
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen size_t size;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data == parser->start)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
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 parser->end = parser->start + size;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (size > 0) {
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 = 0;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen } else {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->highwater_offset = parser->input->v_offset;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstruct json_parser *json_parser_init(struct istream *input)
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen{
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen return json_parser_init_flags(input, 0);
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen}
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainenstruct json_parser *json_parser_init_flags(struct istream *input,
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen enum json_parser_flags flags)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen struct json_parser *parser;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser = i_new(struct json_parser, 1);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->input = input;
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen parser->flags = flags;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->value = str_new(default_pool, 128);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen i_array_init(&parser->nesting, 8);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_stream_ref(input);
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen parser->state = JSON_STATE_VALUE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return parser;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenint json_parser_deinit(struct json_parser **_parser, const char **error_r)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen struct json_parser *parser = *_parser;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *_parser = NULL;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->error != NULL) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* actual parser error */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *error_r = parser->error;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen } else if (parser->input->stream_errno != 0) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_stream_get_name(parser->input));
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen } else if (parser->data == parser->end &&
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen !i_stream_have_bytes_left(parser->input) &&
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->state != JSON_STATE_DONE) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *error_r = "Missing '}'";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen } else {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *error_r = NULL;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_stream_unref(&parser->input);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen array_free(&parser->nesting);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_free(&parser->value);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen i_free(parser);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return *error_r != NULL ? -1 : 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic bool json_parse_whitespace(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen for (; parser->data != parser->end; parser->data++) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen switch (*parser->data) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case ' ':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '\t':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '\r':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '\n':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen default:
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return TRUE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return FALSE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenstatic int json_skip_string(struct json_parser *parser)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen{
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen for (; parser->data != parser->end; parser->data++) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (*parser->data == '"') {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen parser->data++;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen json_parser_update_input_pos(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 1;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (*parser->data == '\\') {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen switch (*++parser->data) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case '"':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case '\\':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case '/':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 'b':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 'f':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 'n':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 'r':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 't':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen break;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen case 'u':
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->end - parser->data < 4)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return -1;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen parser->data += 3;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen break;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen default:
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return -1;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen json_parser_update_input_pos(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen}
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenstatic int json_parse_string(struct json_parser *parser, bool allow_skip,
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen const char **value_r)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen if (*parser->data != '"')
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return -1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->data++;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping && allow_skip) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen *value_r = NULL;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return json_skip_string(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_truncate(parser->value, 0);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen for (; parser->data != parser->end; parser->data++) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '"') {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->data++;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *value_r = str_c(parser->value);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data != '\\')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen else {
b1b8ac3b3c667405e1533aa6db26e55218d26cc6Timo Sirainen if (++parser->data == parser->end)
b1b8ac3b3c667405e1533aa6db26e55218d26cc6Timo Sirainen return 0;
b1b8ac3b3c667405e1533aa6db26e55218d26cc6Timo Sirainen switch (*parser->data) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '"':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '\\':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case '/':
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 'b':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_append_c(parser->value, '\b');
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 'f':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_append_c(parser->value, '\f');
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 'n':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_append_c(parser->value, '\n');
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 'r':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_append_c(parser->value, '\r');
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 't':
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_append_c(parser->value, '\t');
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case 'u':
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen parser->data++;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (parser->end - parser->data < 4) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen /* wait for more data */
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen parser->data = parser->end;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen uni_ucs4_to_utf8_c(hex2dec(parser->data, 4),
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->value);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->data += 3;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen default:
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic int
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenjson_parse_digits(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (parser->data == parser->end)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (*parser->data < '0' || *parser->data > '9')
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen while (parser->data != parser->end &&
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *parser->data >= '0' && *parser->data <= '9')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic int json_parse_int(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen int ret;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '-') {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data == parser->end)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '0')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen else {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return ret;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen int ret;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen str_truncate(parser->value, 0);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if ((ret = json_parse_int(parser)) <= 0)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return ret;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data != parser->end && *parser->data == '.') {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen /* frac */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return ret;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data != parser->end &&
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen /* exp */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data == parser->end)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen str_append_c(parser->value, *parser->data++);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return ret;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *value_r = str_c(parser->value);
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen unsigned int avail, len = strlen(atom);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen avail = parser->end - parser->data;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (avail < len) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (memcmp(parser->data, atom, avail) != 0)
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return -1;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen /* everything matches so far, but we need more data */
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen parser->data += avail;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 0;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen if (memcmp(parser->data, atom, len) != 0)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->data += len;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return 1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic int json_parse_denest(struct json_parser *parser)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen const enum json_state *nested_states;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen unsigned count;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->data++;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen nested_states = array_get(&parser->nesting, &count);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen i_assert(count > 0);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen if (count == 1) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen /* closing root */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = JSON_STATE_DONE;
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen return 0;
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen /* we want to return the ending "]" or "}" to caller */
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen return 1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
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 Sirainen array_delete(&parser->nesting, count-1, 1);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (parser->nested_skip_count > 0) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->nested_skip_count--;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen }
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 1;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen}
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic int
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenjson_parse_close_object(struct json_parser *parser, enum json_type *type_r)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen{
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (json_parse_denest(parser) == 0)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen *type_r = JSON_TYPE_OBJECT_END;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 1;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen}
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic int
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenjson_parse_close_array(struct json_parser *parser, enum json_type *type_r)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen{
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (json_parse_denest(parser) == 0)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen *type_r = JSON_TYPE_ARRAY_END;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainenstatic void json_parser_object_open(struct json_parser *parser)
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen{
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen parser->data++;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen parser->state = JSON_STATE_OBJECT_OPEN;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen json_parser_update_input_pos(parser);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen}
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic int
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char **value_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen{
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen bool skipping = parser->skipping;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen int ret;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (!json_parse_whitespace(parser))
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen switch (parser->state) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case JSON_STATE_ROOT:
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data != '{') {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->error = "Object doesn't begin with '{'";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen json_parser_object_open(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case JSON_STATE_OBJECT_VALUE:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_ARRAY_VALUE:
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen case JSON_STATE_VALUE:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (*parser->data == '{') {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen json_parser_object_open(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen parser->nested_skip_count++;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *type_r = JSON_TYPE_OBJECT;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 1;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen } else if (*parser->data == '[') {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->data++;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = JSON_STATE_ARRAY_OPEN;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen array_append(&parser->nesting, &parser->state, 1);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen json_parser_update_input_pos(parser);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (parser->skipping) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->nested_skip_count++;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen }
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen *type_r = JSON_TYPE_ARRAY;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 1;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen }
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen *type_r = JSON_TYPE_STRING;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_number(parser, value_r)) >= 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen *type_r = JSON_TYPE_NUMBER;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "true")) >= 0) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *type_r = JSON_TYPE_TRUE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *value_r = "true";
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "false")) >= 0) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *type_r = JSON_TYPE_FALSE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *value_r = "false";
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen } else if ((ret = json_parse_atom(parser, "null")) >= 0) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *type_r = JSON_TYPE_NULL;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *value_r = NULL;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen } else {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Invalid data as value";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (ret == 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen i_assert(parser->data == parser->end);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping && *type_r == JSON_TYPE_STRING) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen /* a large string that we want to skip over. */
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen json_parser_update_input_pos(parser);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_SKIP_STRING :
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_ARRAY_SKIP_STRING;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen return -1;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen }
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen switch (parser->state) {
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen case JSON_STATE_OBJECT_VALUE:
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen parser->state = JSON_STATE_OBJECT_NEXT;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen break;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen case JSON_STATE_ARRAY_VALUE:
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen parser->state = JSON_STATE_ARRAY_NEXT;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen break;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen case JSON_STATE_VALUE:
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen parser->state = JSON_STATE_DONE;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen break;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen default:
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen i_unreached();
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen case JSON_STATE_OBJECT_OPEN:
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '}')
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_object(parser, type_r);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->state = JSON_STATE_OBJECT_KEY;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* fall through */
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case JSON_STATE_OBJECT_KEY:
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (json_parse_string(parser, FALSE, value_r) <= 0) {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Expected string as object key";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *type_r = JSON_TYPE_OBJECT_KEY;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->state = JSON_STATE_OBJECT_COLON;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen break;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case JSON_STATE_OBJECT_COLON:
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen if (*parser->data != ':') {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Expected ':' after key";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->data++;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->state = JSON_STATE_OBJECT_VALUE;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_OBJECT_NEXT:
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen /* we skipped over the previous value */
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen parser->skipping = FALSE;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data == '}')
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_object(parser, type_r);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (*parser->data != ',') {
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Expected ',' or '}' after object value";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->state = JSON_STATE_OBJECT_KEY;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->data++;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_ARRAY_OPEN:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (*parser->data == ']')
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_array(parser, type_r);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = JSON_STATE_ARRAY_VALUE;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_ARRAY_NEXT:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen /* we skipped over the previous value */
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->skipping = FALSE;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen }
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen case JSON_STATE_ARRAY_NEXT_SKIP:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (*parser->data == ']')
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return json_parse_close_array(parser, type_r);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (*parser->data != ',') {
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->error = "Expected ',' or '}' after array value";
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return -1;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen }
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = JSON_STATE_ARRAY_VALUE;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->data++;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen json_parser_update_input_pos(parser);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return 0;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_OBJECT_SKIP_STRING:
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen case JSON_STATE_ARRAY_SKIP_STRING:
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (json_skip_string(parser) <= 0)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ?
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return 0;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen case JSON_STATE_DONE:
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen parser->error = "Unexpected data at the end";
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen json_parser_update_input_pos(parser);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen return skipping ? 0 : 1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenint json_parse_next(struct json_parser *parser, enum json_type *type_r,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char **value_r)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen{
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen int ret;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_assert(parser->strinput == NULL);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *value_r = NULL;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen ;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen if (ret > 0)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen break;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (parser->data != parser->end)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return -1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* parsing probably failed because there wasn't enough input.
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen reset the error and try reading more. */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen parser->error = NULL;
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen parser->highwater_offset = parser->input->v_offset +
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen i_stream_get_data_size(parser->input);
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen return ret;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen}
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainenvoid json_parse_skip_next(struct json_parser *parser)
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen{
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_assert(!parser->skipping);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_assert(parser->strinput == NULL);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state == JSON_STATE_OBJECT_VALUE ||
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen parser->state == JSON_STATE_ARRAY_VALUE ||
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen parser->state == JSON_STATE_ARRAY_NEXT);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen parser->skipping = TRUE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen if (parser->state == JSON_STATE_ARRAY_NEXT)
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen parser->state = JSON_STATE_ARRAY_NEXT_SKIP;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen}
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstatic void json_strinput_destroyed(struct json_parser *parser)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen{
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_assert(parser->strinput != NULL);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->strinput = NULL;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen}
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstatic int
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenjson_try_parse_stream_start(struct json_parser *parser,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen struct istream **input_r)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen{
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (!json_parse_whitespace(parser))
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (parser->state == JSON_STATE_OBJECT_COLON) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (*parser->data != ':') {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->error = "Expected ':' after key";
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->data++;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->state = JSON_STATE_OBJECT_VALUE;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (!json_parse_whitespace(parser))
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (*parser->data != '"')
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->data++;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen json_parser_update_input_pos(parser);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
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 Sirainen json_strinput_destroyed, parser);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen *input_r = parser->strinput;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return 1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen}
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenint json_parse_next_stream(struct json_parser *parser,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen struct istream **input_r)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen{
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen int ret;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_assert(!parser->skipping);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_assert(parser->strinput == NULL);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state == JSON_STATE_OBJECT_VALUE ||
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen parser->state == JSON_STATE_ARRAY_VALUE);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen *input_r = NULL;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (json_try_parse_stream_start(parser, input_r) == 0)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen break;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (parser->data != parser->end)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return -1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen /* parsing probably failed because there wasn't enough input.
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen reset the error and try reading more. */
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen parser->error = NULL;
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen parser->highwater_offset = parser->input->v_offset +
f06cc4cb6542c49430ed96b1a1459a2952d820c3Timo Sirainen i_stream_get_data_size(parser->input);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return ret;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen}
4ca2ccd50ad25260865f561e4ebf36899d7fe966Timo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenstatic void json_append_escaped_char(string_t *dest, unsigned char src)
4ca2ccd50ad25260865f561e4ebf36899d7fe966Timo Sirainen{
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen switch (src) {
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\b':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\b");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\f':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\f");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\n':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\n");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\r':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\r");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\t':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\t");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '"':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\\"");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen case '\\':
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append(dest, "\\\\");
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen default:
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen if (src < 32)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_printfa(dest, "\\u%04x", src);
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen else
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen str_append_c(dest, src);
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen break;
4ca2ccd50ad25260865f561e4ebf36899d7fe966Timo Sirainen }
4ca2ccd50ad25260865f561e4ebf36899d7fe966Timo Sirainen}
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenvoid json_append_escaped(string_t *dest, const char *src)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen{
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen for (; *src != '\0'; src++)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen json_append_escaped_char(dest, *src);
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen}
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainenvoid json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen{
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen unsigned int i;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen for (i = 0; i < size; i++)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen json_append_escaped_char(dest, src[i]);
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen}