json-parser.c revision a18503d5dc0751a1f9785e48438a219d95c0b9c2
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "array.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "str.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "hex-dec.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "unichar.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream-jsonstr.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "json-parser.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenenum json_state {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ROOT = 0,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_OPEN,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_KEY,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_COLON,
25d624dd86700c82cd28427f3d3bebe7c8f7f459Timo Sirainen JSON_STATE_OBJECT_VALUE,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_SKIP_STRING,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_NEXT,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ARRAY_OPEN,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ARRAY_VALUE,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ARRAY_SKIP_STRING,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ARRAY_NEXT,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_VALUE,
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen JSON_STATE_DONE
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct json_parser {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream *input;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t highwater_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum json_parser_flags flags;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const unsigned char *start, *end, *data;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *error;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen string_t *value;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream *strinput;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum json_state state;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen ARRAY(enum json_state) nesting;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int nested_skip_count;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool skipping;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool seen_eof;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen uoff_t cur_highwater = parser->input->v_offset +
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen i_stream_get_data_size(parser->input);
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen size_t size;
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen ssize_t ret;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->error != NULL)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen if (parser->highwater_offset == cur_highwater) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = i_stream_read(parser->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret == -2) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen parser->error = "Token too large";
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (ret < 0 && !parser->seen_eof &&
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_get_data_size(parser->input) > 0 &&
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen parser->input->stream_errno == 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* call it once more to finish any pending number */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->seen_eof = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (ret <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen cur_highwater = parser->input->v_offset +
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen i_stream_get_data_size(parser->input);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen i_assert(parser->highwater_offset < cur_highwater);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen parser->highwater_offset = cur_highwater;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen }
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen }
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->end = parser->start + size;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen i_assert(size > 0);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen return 1;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t size;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data == parser->start)
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen return;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
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 parser->end = parser->start + size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (size > 0) {
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 = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->highwater_offset = parser->input->v_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct json_parser *json_parser_init(struct istream *input)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return json_parser_init_flags(input, 0);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct json_parser *json_parser_init_flags(struct istream *input,
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen enum json_parser_flags flags)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct json_parser *parser;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser = i_new(struct json_parser, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->input = input;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->flags = flags;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->value = str_new(default_pool, 128);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_array_init(&parser->nesting, 8);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_ref(input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = JSON_STATE_VALUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return parser;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parser_deinit(struct json_parser **_parser, const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen struct json_parser *parser = *_parser;
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *_parser = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->error != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* actual parser error */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = parser->error;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (parser->input->stream_errno != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_get_name(parser->input));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (parser->data == parser->end &&
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen !i_stream_have_bytes_left(parser->input) &&
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen parser->state != JSON_STATE_DONE) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen *error_r = "Missing '}'";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_unref(&parser->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen array_free(&parser->nesting);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_free(&parser->value);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return *error_r != NULL ? -1 : 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool json_parse_whitespace(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (; parser->data != parser->end; parser->data++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen switch (*parser->data) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case ' ':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\t':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\r':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\n':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen default:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_skip_string(struct json_parser *parser)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen for (; parser->data != parser->end; parser->data++) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*parser->data == '"') {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->data++;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen json_parser_update_input_pos(parser);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return 1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*parser->data == '\\') {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen switch (*++parser->data) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case '"':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case '\\':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case '/':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 'b':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 'f':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 'n':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 'r':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 't':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen break;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case 'u':
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (parser->end - parser->data < 4)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->data += 3;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen break;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen default:
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen json_parser_update_input_pos(parser);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return 0;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenstatic int json_parse_string(struct json_parser *parser, bool allow_skip,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const char **value_r)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (*parser->data != '"')
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return -1;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen parser->data++;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping && allow_skip) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *value_r = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return json_skip_string(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen str_truncate(parser->value, 0);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen for (; parser->data != parser->end; parser->data++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data == '"') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data++;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen *value_r = str_c(parser->value);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return 1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (*parser->data != '\\')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (++parser->data == parser->end)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return 0;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen switch (*parser->data) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case '"':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case '\\':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case '/':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen str_append_c(parser->value, *parser->data);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen break;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case 'b':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen str_append_c(parser->value, '\b');
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen break;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen case 'f':
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen str_append_c(parser->value, '\f');
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen break;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case 'n':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen str_append_c(parser->value, '\n');
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen break;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen case 'r':
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen str_append_c(parser->value, '\r');
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen break;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen case 't':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, '\t');
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen break;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen case 'u':
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen parser->data++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->end - parser->data < 4) {
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen /* wait for more data */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data = parser->end;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen uni_ucs4_to_utf8_c(hex2dec(parser->data, 4),
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen parser->value);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen parser->data += 3;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen break;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen default:
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return -1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen}
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenstatic int
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenjson_parse_digits(struct json_parser *parser)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data == parser->end)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data < '0' || *parser->data > '9')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen while (parser->data != parser->end &&
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen *parser->data >= '0' && *parser->data <= '9')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_int(struct json_parser *parser)
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen{
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data == '-') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data == parser->end)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data == '0')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return ret;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen return 1;
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_truncate(parser->value, 0);
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if ((ret = json_parse_int(parser)) <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data != parser->end && *parser->data == '.') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* frac */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen if (parser->data != parser->end &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* exp */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen if (parser->data == parser->end)
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen return 0;
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append_c(parser->value, *parser->data++);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return ret;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen *value_r = str_c(parser->value);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int avail, len = strlen(atom);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen avail = parser->end - parser->data;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (avail < len) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (memcmp(parser->data, atom, avail) != 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen /* everything matches so far, but we need more data */
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen parser->data += avail;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return 0;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen }
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen if (memcmp(parser->data, atom, len) != 0)
e593e507ee5ea3869271a631874c5c4b5c7a294dTimo Sirainen return -1;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen parser->data += len;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen return 1;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int json_parse_denest(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen const enum json_state *nested_states;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned count;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen nested_states = array_get(&parser->nesting, &count);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(count > 0);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (count == 1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* closing root */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = JSON_STATE_DONE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we want to return the ending "]" or "}" to caller */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen
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 Sirainen array_delete(&parser->nesting, count-1, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->nested_skip_count > 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->nested_skip_count--;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_parse_close_object(struct json_parser *parser, enum json_type *type_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (json_parse_denest(parser) == 0)
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *type_r = JSON_TYPE_OBJECT_END;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_parse_close_array(struct json_parser *parser, enum json_type *type_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (json_parse_denest(parser) == 0)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen *type_r = JSON_TYPE_ARRAY_END;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void json_parser_object_open(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = JSON_STATE_OBJECT_OPEN;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **value_r)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen{
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen bool skipping = parser->skipping;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen int ret;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (!json_parse_whitespace(parser))
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return -1;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen switch (parser->state) {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen case JSON_STATE_ROOT:
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (*parser->data != '{') {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen parser->error = "Object doesn't begin with '{'";
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return -1;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen json_parser_object_open(parser);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen case JSON_STATE_OBJECT_VALUE:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_ARRAY_VALUE:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_VALUE:
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen if (*parser->data == '{') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_object_open(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->nested_skip_count++;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *type_r = JSON_TYPE_OBJECT;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } else if (*parser->data == '[') {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen parser->data++;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen parser->state = JSON_STATE_ARRAY_OPEN;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen array_append(&parser->nesting, &parser->state, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (parser->skipping) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen parser->nested_skip_count++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *type_r = JSON_TYPE_ARRAY;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen return 1;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen }
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen *type_r = JSON_TYPE_STRING;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen } else if ((ret = json_parse_number(parser, value_r)) >= 0) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen *type_r = JSON_TYPE_NUMBER;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "true")) >= 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *type_r = JSON_TYPE_TRUE;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen *value_r = "true";
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "false")) >= 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen *type_r = JSON_TYPE_FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *value_r = "false";
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else if ((ret = json_parse_atom(parser, "null")) >= 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *type_r = JSON_TYPE_NULL;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen *value_r = NULL;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen parser->error = "Invalid data as value";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(parser->data == parser->end);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping && *type_r == JSON_TYPE_STRING) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* a large string that we want to skip over. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_SKIP_STRING :
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_ARRAY_SKIP_STRING;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen switch (parser->state) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_OBJECT_VALUE:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = JSON_STATE_OBJECT_NEXT;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_ARRAY_VALUE:
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->state = JSON_STATE_ARRAY_NEXT;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen break;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case JSON_STATE_VALUE:
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->state = JSON_STATE_DONE;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen break;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen default:
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_unreached();
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen break;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen case JSON_STATE_OBJECT_OPEN:
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (*parser->data == '}')
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return json_parse_close_object(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->state = JSON_STATE_OBJECT_KEY;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* fall through */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen case JSON_STATE_OBJECT_KEY:
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (json_parse_string(parser, FALSE, value_r) <= 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected string as object key";
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *type_r = JSON_TYPE_OBJECT_KEY;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->state = JSON_STATE_OBJECT_COLON;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen break;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen case JSON_STATE_OBJECT_COLON:
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*parser->data != ':') {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->error = "Expected ':' after key";
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->data++;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->state = JSON_STATE_OBJECT_VALUE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen json_parser_update_input_pos(parser);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen case JSON_STATE_OBJECT_NEXT:
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* we skipped over the previous value */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->skipping = FALSE;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (*parser->data == '}')
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return json_parse_close_object(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (*parser->data != ',') {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected ',' or '}' after object value";
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->state = JSON_STATE_OBJECT_KEY;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->data++;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen json_parser_update_input_pos(parser);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen case JSON_STATE_ARRAY_OPEN:
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (*parser->data == ']')
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen return json_parse_close_array(parser, type_r);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->state = JSON_STATE_ARRAY_VALUE;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen case JSON_STATE_ARRAY_NEXT:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->skipping && parser->nested_skip_count == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we skipped over the previous value */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->skipping = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (*parser->data == ']')
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return json_parse_close_array(parser, type_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data != ',') {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->error = "Expected ',' or '}' after array value";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = JSON_STATE_ARRAY_VALUE;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen parser->data++;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen json_parser_update_input_pos(parser);
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen return 0;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen case JSON_STATE_OBJECT_SKIP_STRING:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_ARRAY_SKIP_STRING:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (json_skip_string(parser) <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ?
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case JSON_STATE_DONE:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->error = "Unexpected data at the end";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return skipping ? 0 : 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parse_next(struct json_parser *parser, enum json_type *type_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **value_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(parser->strinput == NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *value_r = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
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 ;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (ret > 0)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen break;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (parser->data != parser->end)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return -1;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* parsing probably failed because there wasn't enough input.
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen reset the error and try reading more. */
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen parser->error = NULL;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->highwater_offset = parser->input->v_offset +
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen i_stream_get_data_size(parser->input);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen }
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return ret;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen}
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid json_parse_skip_next(struct json_parser *parser)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen i_assert(!parser->skipping);
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen i_assert(parser->strinput == NULL);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen parser->state == JSON_STATE_OBJECT_VALUE ||
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->state == JSON_STATE_ARRAY_VALUE);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen parser->skipping = TRUE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void json_strinput_destroyed(struct json_parser *parser)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(parser->strinput != NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen parser->strinput = NULL;
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen}
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenstatic int
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenjson_try_parse_stream_start(struct json_parser *parser,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct istream **input_r)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen{
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (!json_parse_whitespace(parser))
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->state == JSON_STATE_OBJECT_COLON) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data != ':') {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen parser->error = "Expected ':' after key";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data++;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen parser->state = JSON_STATE_OBJECT_VALUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!json_parse_whitespace(parser))
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*parser->data != '"')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->data++;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen json_parser_update_input_pos(parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
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 Sirainen json_strinput_destroyed, parser);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *input_r = parser->strinput;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint json_parse_next_stream(struct json_parser *parser,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream **input_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_assert(!parser->skipping);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen i_assert(parser->strinput == NULL);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state == JSON_STATE_OBJECT_VALUE ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->state == JSON_STATE_ARRAY_VALUE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *input_r = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while ((ret = json_parser_read_more(parser)) > 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (json_try_parse_stream_start(parser, input_r) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (parser->data != parser->end)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* parsing probably failed because there wasn't enough input.
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen reset the error and try reading more. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->error = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen parser->highwater_offset = parser->input->v_offset +
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_get_data_size(parser->input);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen}
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstatic void json_append_escaped_char(string_t *dest, unsigned char src)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen switch (src) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen case '\b':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\b");
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\f':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\f");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\n':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\n");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen case '\r':
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen str_append(dest, "\\r");
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen break;
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen case '\t':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\t");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '"':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\\"");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case '\\':
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(dest, "\\\\");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen default:
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (src < 32)
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen str_printfa(dest, "\\u%04x", src);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen str_append_c(dest, src);
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen break;
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainen }
}
void json_append_escaped(string_t *dest, const char *src)
{
for (; *src != '\0'; src++)
json_append_escaped_char(dest, *src);
}
void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size)
{
unsigned int i;
for (i = 0; i < size; i++)
json_append_escaped_char(dest, src[i]);
}