json-parser.c revision 4e8e7a93628b4ed60aaaa47c6f72c1433f21e81d
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "str.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "istream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hex-dec.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "unichar.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "json-parser.h"
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenenum json_state {
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen JSON_STATE_ROOT = 0,
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen JSON_STATE_OBJECT_OPEN,
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen JSON_STATE_OBJECT_KEY,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen JSON_STATE_OBJECT_COLON,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen JSON_STATE_OBJECT_VALUE,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen JSON_STATE_OBJECT_VALUE_NEXT,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen JSON_STATE_DONE
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen};
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstruct json_parser {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct istream *input;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen uoff_t highwater_offset;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const unsigned char *start, *end, *data;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *error;
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen string_t *value;
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen enum json_state state;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int nested_object_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **value_r);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int json_parser_read_more(struct json_parser *parser)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uoff_t cur_highwater = parser->input->v_offset +
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_stream_get_data_size(parser->input);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen size_t size;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ssize_t ret;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_assert(parser->highwater_offset <= cur_highwater);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (parser->error != NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen if (parser->highwater_offset == cur_highwater) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ret = i_stream_read(parser->input);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (ret == -2) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen parser->error = "Token too large";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (ret <= 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return ret;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen cur_highwater = parser->input->v_offset +
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen i_stream_get_data_size(parser->input);
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen i_assert(parser->highwater_offset < cur_highwater);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen parser->highwater_offset = cur_highwater;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen }
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen parser->end = parser->start + size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(size > 0);
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen return 1;
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen}
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic void json_parser_update_input_pos(struct json_parser *parser)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (parser->data == parser->start)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen return;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(parser->input, parser->data - parser->start);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen parser->start = parser->data = i_stream_get_data(parser->input, &size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen parser->end = parser->start + size;
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen if (size > 0) {
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen /* we skipped over some data and there's still data left.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen no need to read() the next time. */
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen parser->highwater_offset = 0;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen } else {
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen parser->highwater_offset = parser->input->v_offset;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen }
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen}
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainenstruct json_parser *json_parser_init(struct istream *input)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen{
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen struct json_parser *parser;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen parser = i_new(struct json_parser, 1);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen parser->input = input;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen parser->value = str_new(default_pool, 128);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_stream_ref(input);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return parser;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen}
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenint json_parser_deinit(struct json_parser **_parser, const char **error_r)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen{
4b41116563110d00330896a568eff1078c382827Timo Sirainen struct json_parser *parser = *_parser;
4b41116563110d00330896a568eff1078c382827Timo Sirainen
4b41116563110d00330896a568eff1078c382827Timo Sirainen *_parser = NULL;
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen if (parser->error != NULL) {
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen /* actual parser error */
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen *error_r = parser->error;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (parser->input->stream_errno != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_stream_get_name(parser->input));
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen } else if (parser->data == parser->end &&
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen !i_stream_have_bytes_left(parser->input) &&
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen parser->state != JSON_STATE_ROOT &&
ccec5f82349eae44087900c0e64ed1fd5a1a6fcaTimo Sirainen parser->state != JSON_STATE_DONE) {
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen *error_r = "Missing '}'";
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_stream_unref(&parser->input);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen str_free(&parser->value);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen i_free(parser);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return *error_r != NULL ? -1 : 0;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen}
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainenstatic bool json_parse_whitespace(struct json_parser *parser)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen for (; parser->data != parser->end; parser->data++) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen switch (*parser->data) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case ' ':
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case '\t':
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case '\r':
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen case '\n':
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen break;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen default:
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen json_parser_update_input_pos(parser);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen json_parser_update_input_pos(parser);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainenstatic int json_parse_string(struct json_parser *parser, const char **value_r)
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen{
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen if (*parser->data != '"')
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen parser->data++;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen str_truncate(parser->value, 0);
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen for (; parser->data != parser->end; parser->data++) {
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (*parser->data == '"') {
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen parser->data++;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen *value_r = str_c(parser->value);
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen return 1;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen }
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen if (*parser->data != '\\')
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen str_append_c(parser->value, *parser->data);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen else {
c13f3df87bc8ec1fb279fc0ffa6e8517f74dc07cTimo Sirainen switch (*++parser->data) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case '"':
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case '\\':
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen case '/':
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append_c(parser->value, *parser->data);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen case 'b':
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_append_c(parser->value, '\b');
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen break;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen case 'f':
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_append_c(parser->value, '\f');
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen break;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case 'n':
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, '\n');
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen break;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case 'r':
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, '\r');
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen break;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case 't':
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen str_append_c(parser->value, '\t');
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen break;
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen case 'u':
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen parser->data++;
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen if (parser->end - parser->data < 4) {
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen /* wait for more data */
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen parser->data = parser->end;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen return 0;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen }
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen uni_ucs4_to_utf8_c(hex2dec(parser->data, 4),
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen parser->value);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen parser->data += 3;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen break;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen default:
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen return -1;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic int
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenjson_parse_digits(struct json_parser *parser)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (parser->data == parser->end)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (*parser->data < '0' || *parser->data > '9')
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return -1;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen while (parser->data != parser->end &&
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen *parser->data >= '0' && *parser->data <= '9')
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, *parser->data++);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 1;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen}
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainenstatic int json_parse_int(struct json_parser *parser)
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen int ret;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen if (*parser->data == '-') {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, *parser->data++);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (parser->data == parser->end)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (*parser->data == '0')
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, *parser->data++);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen else {
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return ret;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return 1;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic int json_parse_number(struct json_parser *parser, const char **value_r)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen int ret;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_truncate(parser->value, 0);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if ((ret = json_parse_int(parser)) <= 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return ret;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (parser->data != parser->end && *parser->data == '.') {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* frac */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append_c(parser->value, *parser->data++);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return ret;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (parser->data != parser->end &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (*parser->data == 'e' || *parser->data == 'E')) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* exp */
e4c90f0b88e40a8f92b8f5e1f1a3ea701e5c965cTimo Sirainen str_append_c(parser->value, *parser->data++);
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen if (parser->data == parser->end)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*parser->data == '+' || *parser->data == '-')
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_append_c(parser->value, *parser->data++);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if ((ret = json_parse_digits(parser)) <= 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return ret;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (parser->data == parser->end && !parser->input->eof)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen *value_r = str_c(parser->value);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int json_parse_atom(struct json_parser *parser, const char *atom)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int avail, len = strlen(atom);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen avail = parser->end - parser->data;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (avail < len) {
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen if (memcmp(parser->data, atom, avail) != 0)
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen return -1;
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen /* everything matches so far, but we need more data */
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen parser->data += avail;
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (memcmp(parser->data, atom, len) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen parser->data += len;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 1;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen}
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainenstatic int
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenjson_parse_object_close(struct json_parser *parser, enum json_type *type_r,
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen const char **value_r)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen{
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen parser->data++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen json_parser_update_input_pos(parser);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (parser->nested_object_count > 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* closing a nested object */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen parser->nested_object_count--;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen parser->state = JSON_STATE_OBJECT_VALUE_NEXT;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen *type_r = JSON_TYPE_OBJECT_END;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return 0;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen parser->state = JSON_STATE_DONE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return json_try_parse_next(parser, type_r, value_r);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenjson_try_parse_next(struct json_parser *parser, enum json_type *type_r,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char **value_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!json_parse_whitespace(parser))
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch (parser->state) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case JSON_STATE_ROOT:
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen if (*parser->data != '{') {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen parser->error = "Object doesn't begin with '{'";
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen return -1;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen }
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen parser->data++;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen parser->state = JSON_STATE_OBJECT_OPEN;
b3b4f3875850099c9292ad74d08bb385c3988f8fTimo Sirainen json_parser_update_input_pos(parser);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return json_try_parse_next(parser, type_r, value_r);
case JSON_STATE_OBJECT_VALUE:
if (*parser->data == '[') {
parser->error = "Arrays not supported";
return -1;
} else if (*parser->data == '{') {
parser->data++;
parser->state = JSON_STATE_OBJECT_OPEN;
parser->nested_object_count++;
json_parser_update_input_pos(parser);
*type_r = JSON_TYPE_OBJECT;
return 0;
}
if ((ret = json_parse_string(parser, value_r)) >= 0) {
*type_r = JSON_TYPE_STRING;
} else if ((ret = json_parse_number(parser, value_r)) >= 0) {
*type_r = JSON_TYPE_NUMBER;
} else if ((ret = json_parse_atom(parser, "true")) >= 0) {
*type_r = JSON_TYPE_TRUE;
*value_r = "true";
} else if ((ret = json_parse_atom(parser, "false")) >= 0) {
*type_r = JSON_TYPE_FALSE;
*value_r = "false";
} else if ((ret = json_parse_atom(parser, "null")) >= 0) {
*type_r = JSON_TYPE_NULL;
*value_r = NULL;
} else {
parser->error = "Invalid data as value";
return -1;
}
if (ret == 0) {
i_assert(parser->data == parser->end);
return -1;
}
parser->state = parser->state == JSON_STATE_ROOT ?
JSON_STATE_DONE :
JSON_STATE_OBJECT_VALUE_NEXT;
break;
case JSON_STATE_OBJECT_OPEN:
if (*parser->data == '}')
return json_parse_object_close(parser, type_r, value_r);
parser->state = JSON_STATE_OBJECT_KEY;
/* fall through */
case JSON_STATE_OBJECT_KEY:
if (json_parse_string(parser, value_r) <= 0) {
parser->error = "Expected string as object key";
return -1;
}
*type_r = JSON_TYPE_OBJECT_KEY;
parser->state = JSON_STATE_OBJECT_COLON;
break;
case JSON_STATE_OBJECT_COLON:
if (*parser->data != ':') {
parser->error = "Expected ':' after key";
return -1;
}
parser->data++;
parser->state = JSON_STATE_OBJECT_VALUE;
json_parser_update_input_pos(parser);
return json_try_parse_next(parser, type_r, value_r);
case JSON_STATE_OBJECT_VALUE_NEXT:
if (*parser->data == '}')
return json_parse_object_close(parser, type_r, value_r);
if (*parser->data != ',') {
parser->error = "Expected ',' or '}' after object value";
return -1;
}
parser->state = JSON_STATE_OBJECT_KEY;
parser->data++;
json_parser_update_input_pos(parser);
return json_try_parse_next(parser, type_r, value_r);
case JSON_STATE_DONE:
parser->error = "Unexpected data at the end";
return -1;
}
json_parser_update_input_pos(parser);
return 0;
}
int json_parse_next(struct json_parser *parser, enum json_type *type_r,
const char **value_r)
{
int ret;
*value_r = NULL;
while ((ret = json_parser_read_more(parser)) > 0) {
if (json_try_parse_next(parser, type_r, value_r) == 0)
break;
if (parser->data != parser->end)
return -1;
/* parsing probably failed because there wasn't enough input.
reset the error and try reading more. */
parser->error = NULL;
}
return ret;
}