Changeset View
Changeset View
Standalone View
Standalone View
src/univalue/lib/univalue_read.cpp
// Copyright 2014 BitPay Inc. | // Copyright 2014 BitPay Inc. | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or https://opensource.org/licenses/mit-license.php. | // file COPYING or https://opensource.org/licenses/mit-license.php. | ||||
#include <string.h> | #include <string.h> | ||||
#include <vector> | #include <vector> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include "univalue.h" | #include "univalue.h" | ||||
#include "univalue_utffilter.h" | #include "univalue_utffilter.h" | ||||
/* | |||||
* According to stackexchange, the original json test suite wanted | |||||
* to limit depth to 22. Widely-deployed PHP bails at depth 512, | |||||
* so we will follow PHP's lead, which should be more than sufficient | |||||
* (further stackexchange comments indicate depth > 32 rarely occurs). | |||||
*/ | |||||
static const size_t MAX_JSON_DEPTH = 512; | |||||
static bool json_isdigit(int ch) | static bool json_isdigit(int ch) | ||||
{ | { | ||||
return ((ch >= '0') && (ch <= '9')); | return ((ch >= '0') && (ch <= '9')); | ||||
} | } | ||||
// convert hexadecimal string to unsigned integer | // convert hexadecimal string to unsigned integer | ||||
static const char *hatoui(const char *first, const char *last, | static const char *hatoui(const char *first, const char *last, | ||||
unsigned int& out) | unsigned int& out) | ||||
▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | bool UniValue::read(const char *raw, size_t size) | ||||
enum jtokentype tok = JTOK_NONE; | enum jtokentype tok = JTOK_NONE; | ||||
enum jtokentype last_tok = JTOK_NONE; | enum jtokentype last_tok = JTOK_NONE; | ||||
const char* end = raw + size; | const char* end = raw + size; | ||||
do { | do { | ||||
last_tok = tok; | last_tok = tok; | ||||
tok = getJsonToken(tokenVal, consumed, raw, end); | tok = getJsonToken(tokenVal, consumed, raw, end); | ||||
if (tok == JTOK_NONE || tok == JTOK_ERR) | if (tok == JTOK_NONE || tok == JTOK_ERR) | ||||
return false; | goto return_fail; | ||||
raw += consumed; | raw += consumed; | ||||
bool isValueOpen = jsonTokenIsValue(tok) || | bool isValueOpen = jsonTokenIsValue(tok) || | ||||
tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN; | tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN; | ||||
if (expect(VALUE)) { | if (expect(VALUE)) { | ||||
if (!isValueOpen) | if (!isValueOpen) | ||||
return false; | goto return_fail; | ||||
clearExpect(VALUE); | clearExpect(VALUE); | ||||
} else if (expect(ARR_VALUE)) { | } else if (expect(ARR_VALUE)) { | ||||
bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE); | bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE); | ||||
if (!isArrValue) | if (!isArrValue) | ||||
return false; | goto return_fail; | ||||
clearExpect(ARR_VALUE); | clearExpect(ARR_VALUE); | ||||
} else if (expect(OBJ_NAME)) { | } else if (expect(OBJ_NAME)) { | ||||
bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING); | bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING); | ||||
if (!isObjName) | if (!isObjName) | ||||
return false; | goto return_fail; | ||||
} else if (expect(COLON)) { | } else if (expect(COLON)) { | ||||
if (tok != JTOK_COLON) | if (tok != JTOK_COLON) | ||||
return false; | goto return_fail; | ||||
clearExpect(COLON); | clearExpect(COLON); | ||||
} else if (!expect(COLON) && (tok == JTOK_COLON)) { | } else if (!expect(COLON) && (tok == JTOK_COLON)) { | ||||
return false; | goto return_fail; | ||||
} | } | ||||
if (expect(NOT_VALUE)) { | if (expect(NOT_VALUE)) { | ||||
if (isValueOpen) | if (isValueOpen) | ||||
return false; | goto return_fail; | ||||
clearExpect(NOT_VALUE); | clearExpect(NOT_VALUE); | ||||
} | } | ||||
switch (tok) { | switch (tok) { | ||||
case JTOK_OBJ_OPEN: | case JTOK_OBJ_OPEN: | ||||
case JTOK_ARR_OPEN: { | case JTOK_ARR_OPEN: { | ||||
VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR); | VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR); | ||||
if (!stack.size()) { | if (!stack.size()) { | ||||
if (utyp == VOBJ) | if (utyp == VOBJ) | ||||
setObject(); | setObject(); | ||||
else | else | ||||
setArray(); | setArray(); | ||||
stack.push_back(this); | stack.push_back(this); | ||||
} else { | } else { | ||||
UniValue tmpVal(utyp); | UniValue tmpVal(utyp); | ||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
top->values.push_back(tmpVal); | top->values.push_back(tmpVal); | ||||
UniValue *newTop = &(top->values.back()); | UniValue *newTop = &(top->values.back()); | ||||
stack.push_back(newTop); | stack.push_back(newTop); | ||||
} | } | ||||
if (stack.size() > MAX_JSON_DEPTH) | |||||
goto return_fail; | |||||
if (utyp == VOBJ) | if (utyp == VOBJ) | ||||
setExpect(OBJ_NAME); | setExpect(OBJ_NAME); | ||||
else | else | ||||
setExpect(ARR_VALUE); | setExpect(ARR_VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_OBJ_CLOSE: | case JTOK_OBJ_CLOSE: | ||||
case JTOK_ARR_CLOSE: { | case JTOK_ARR_CLOSE: { | ||||
if (!stack.size() || (last_tok == JTOK_COMMA)) | if (!stack.size() || (last_tok == JTOK_COMMA)) | ||||
return false; | goto return_fail; | ||||
VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR); | VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR); | ||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
if (utyp != top->getType()) | if (utyp != top->getType()) | ||||
return false; | goto return_fail; | ||||
stack.pop_back(); | stack.pop_back(); | ||||
clearExpect(OBJ_NAME); | clearExpect(OBJ_NAME); | ||||
setExpect(NOT_VALUE); | setExpect(NOT_VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_COLON: { | case JTOK_COLON: { | ||||
if (!stack.size()) | if (!stack.size()) | ||||
return false; | goto return_fail; | ||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
if (top->getType() != VOBJ) | if (top->getType() != VOBJ) | ||||
return false; | goto return_fail; | ||||
setExpect(VALUE); | setExpect(VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_COMMA: { | case JTOK_COMMA: { | ||||
if (!stack.size() || | if (!stack.size() || | ||||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN)) | (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN)) | ||||
return false; | goto return_fail; | ||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
if (top->getType() == VOBJ) | if (top->getType() == VOBJ) | ||||
setExpect(OBJ_NAME); | setExpect(OBJ_NAME); | ||||
else | else | ||||
setExpect(ARR_VALUE); | setExpect(ARR_VALUE); | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | do { | ||||
top->values.push_back(tmpVal); | top->values.push_back(tmpVal); | ||||
} | } | ||||
setExpect(NOT_VALUE); | setExpect(NOT_VALUE); | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
return false; | goto return_fail; | ||||
} | } | ||||
} while (!stack.empty ()); | } while (!stack.empty ()); | ||||
/* Check that nothing follows the initial construct (parsed above). */ | /* Check that nothing follows the initial construct (parsed above). */ | ||||
tok = getJsonToken(tokenVal, consumed, raw, end); | tok = getJsonToken(tokenVal, consumed, raw, end); | ||||
if (tok != JTOK_NONE) | if (tok != JTOK_NONE) | ||||
return false; | goto return_fail; | ||||
return true; | return true; | ||||
return_fail: | |||||
clear(); | |||||
return false; | |||||
} | } | ||||