/* */ #include "json.h" #include #include "array_fun.h" #include "DlAbortEx.h" #include "error_code.h" #include "a2functional.h" #include "util.h" #include "fmt.h" #include "Base64.h" namespace aria2 { namespace json { // Function prototype declaration namespace { std::pair, std::string::const_iterator> decode (std::string::const_iterator first, std::string::const_iterator last, size_t depth); } // namespace namespace { const char WS[] = { 0x20, 0x09, 0x0a, 0x0d }; const char ESCAPE_CHARS[] = { '"', '\\', '/', '\b', '\f', '\n', '\r', '\t' }; const size_t MAX_STRUCTURE_DEPTH = 100; } // namespace namespace { std::string::const_iterator skipWs (std::string::const_iterator first, std::string::const_iterator last) { while(first != last && std::find(vbegin(WS), vend(WS), *first) != vend(WS)) { ++first; } return first; } } // namespace namespace { void checkEof (std::string::const_iterator first, std::string::const_iterator last) { if(first == last) { throw DL_ABORT_EX2("JSON decoding failed: unexpected EOF", error_code::JSON_PARSE_ERROR); } } } // namespace namespace { std::string::const_iterator decodeKeyword (std::string::const_iterator first, std::string::const_iterator last, const std::string& keyword) { size_t len = keyword.size(); for(size_t i = 0; i < len; ++i) { checkEof(first, last); if(*first != keyword[i]) { throw DL_ABORT_EX2(fmt("JSON decoding failed: %s not found.", keyword.c_str()), error_code::JSON_PARSE_ERROR); } ++first; } return first; } } // namespace namespace { std::pair, std::string::const_iterator> decodeTrue (std::string::const_iterator first, std::string::const_iterator last) { first = decodeKeyword(first, last, "true"); return std::make_pair(Bool::gTrue(), first); } } // namespace namespace { std::pair, std::string::const_iterator> decodeFalse (std::string::const_iterator first, std::string::const_iterator last) { first = decodeKeyword(first, last, "false"); return std::make_pair(Bool::gFalse(), first); } } // namespace namespace { std::pair, std::string::const_iterator> decodeNull (std::string::const_iterator first, std::string::const_iterator last) { first = decodeKeyword(first, last, "null"); return std::make_pair(Null::g(), first); } } // namespace namespace { std::pair, std::string::const_iterator> decodeString (std::string::const_iterator first, std::string::const_iterator last) { // Consume first char, assuming it is '"'. ++first; std::string s; std::string::const_iterator offset = first; while(first != last) { if(*first == '"') { break; } if(*first == '\\') { s += std::string(offset, first); ++first; checkEof(first, last); if(*first == 'u') { ++first; std::string uchars; for(int i = 0; i < 4; ++i, ++first) { checkEof(first, last); uchars += *first; } checkEof(first, last); uint16_t codepoint = util::parseUInt(uchars, 16); if(codepoint <= 0x007fu) { s += static_cast(codepoint); } else if(codepoint <= 0x07ffu) { unsigned char c2 = 0x80u | (codepoint & 0x003fu); unsigned char c1 = 0xC0u | (codepoint >> 6); s += c1; s += c2; } else if(in(codepoint, 0xD800u, 0xDBFFu)) { // surrogate pair if(*first != '\\' || first+1 == last || *(first+1) != 'u') { throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.", error_code::JSON_PARSE_ERROR); } first += 2; std::string uchars; for(int i = 0; i < 4; ++i, ++first) { checkEof(first, last); uchars += *first; } checkEof(first, last); uint16_t codepoint2 = util::parseUInt(uchars, 16); if(!in(codepoint2, 0xDC00u, 0xDFFFu)) { throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.", error_code::JSON_PARSE_ERROR); } uint32_t fullcodepoint = 0x010000u; fullcodepoint += (codepoint & 0x03FFu) << 10; fullcodepoint += (codepoint2 & 0x03FFu); unsigned char c4 = 0x80u | (fullcodepoint & 0x003Fu); unsigned char c3 = 0x80u | ((fullcodepoint >> 6) & 0x003Fu); unsigned char c2 = 0x80u | ((fullcodepoint >> 12) & 0x003Fu); unsigned char c1 = 0xf0u | (fullcodepoint >> 18); s += c1; s += c2; s += c3; s += c4; } else { unsigned char c3 = 0x80u | (codepoint & 0x003Fu); unsigned char c2 = 0x80u | ((codepoint >> 6) & 0x003Fu); unsigned char c1 = 0xE0u | (codepoint >> 12); s += c1; s += c2; s += c3; } offset = first; } else { if(*first == 'b') { s += '\b'; } else if(*first == 'f') { s += '\f'; } else if(*first == 'n') { s += '\n'; } else if(*first == 'r') { s += '\r'; } else if(*first == 't') { s += '\t'; } else { s += *first; } ++first; offset = first; } } else { ++first; } } checkEof(first, last); if(std::distance(offset, first) > 0) { s += std::string(offset, first); } if(!util::isUtf8(s)) { throw DL_ABORT_EX2("JSON decoding failed: Non UTF-8 string.", error_code::JSON_PARSE_ERROR); } ++first; return std::make_pair(String::g(s), first); } } // namespace namespace { void checkEmptyDigit (std::string::const_iterator first, std::string::const_iterator last) { if(std::distance(first, last) == 0) { throw DL_ABORT_EX2("JSON decoding failed: zero DIGIT.", error_code::JSON_PARSE_ERROR); } } } // namespace namespace { void checkLeadingZero (std::string::const_iterator first, std::string::const_iterator last) { if(std::distance(first, last) > 2 && *first == '0') { throw DL_ABORT_EX2("JSON decoding failed: leading zero.", error_code::JSON_PARSE_ERROR); } } } // namespace namespace { std::pair, std::string::const_iterator> decodeNumber (std::string::const_iterator first, std::string::const_iterator last) { std::string s; if(*first == '-') { s += *first; ++first; } std::string::const_iterator offset = first; while(first != last && in(*first, '0', '9')) { ++first; } checkEof(first, last); checkEmptyDigit(offset, first); checkLeadingZero(offset, first); s += std::string(offset, first); bool fp = false; if(*first == '.') { fp = true; s += *first; ++first; offset = first; while(first != last && in(*first, '0', '9')) { ++first; } checkEof(first, last); checkEmptyDigit(offset, first); s += std::string(offset, first); } if(*first == 'e') { fp = true; s += *first; ++first; checkEof(first, last); if(*first == '+' || *first == '-') { s += *first; ++first; } offset = first; while(first != last && in(*first, '0', '9')) { ++first; } checkEof(first, last); checkEmptyDigit(offset, first); s += std::string(offset, first); } if(fp) { // Since we don't have floating point coutner part in ValueBase, // we just treat it as string. return std::make_pair(String::g(s), first); } else { Integer::ValueType val = util::parseLLInt(s); return std::make_pair(Integer::g(val), first); } } } // namespace namespace { void checkDepth(size_t depth) { if(depth >= MAX_STRUCTURE_DEPTH) { throw DL_ABORT_EX2("JSON decoding failed: Structure is too deep.", error_code::JSON_PARSE_ERROR); } } } // namespace namespace { std::pair, std::string::const_iterator> decodeArray (std::string::const_iterator first, std::string::const_iterator last, size_t depth) { checkDepth(depth); SharedHandle list = List::g(); // Consume first char, assuming it is '['. ++first; first = skipWs(first, last); checkEof(first, last); if(*first != ']') { while(1) { std::pair, std::string::const_iterator> r = decode(first, last, depth); list->append(r.first); first = r.second; first = skipWs(first, last); if(first == last || (*first != ',' && *first != ']')) { throw DL_ABORT_EX2("JSON decoding failed:" " value-separator ',' or ']' is not found.", error_code::JSON_PARSE_ERROR); } if(*first == ']') { break; } ++first; } } ++first; return std::make_pair(list, first); } } // namespace namespace { std::pair, std::string::const_iterator> decodeObject (std::string::const_iterator first, std::string::const_iterator last, size_t depth) { checkDepth(depth); SharedHandle dict = Dict::g(); // Consume first char, assuming it is '{' ++first; first = skipWs(first, last); checkEof(first, last); if(*first != '}') { while(1) { std::pair, std::string::const_iterator> keyRet = decodeString(first, last); first = keyRet.second; first = skipWs(first, last); if(first == last || *first != ':') { throw DL_ABORT_EX2("JSON decoding failed:" " name-separator ':' is not found.", error_code::JSON_PARSE_ERROR); } ++first; std::pair, std::string::const_iterator> valueRet = decode(first, last, depth); dict->put(downcast(keyRet.first)->s(), valueRet.first); first = valueRet.second; first = skipWs(first, last); if(first == last || (*first != ',' && *first != '}')) { throw DL_ABORT_EX2("JSON decoding failed:" " value-separator ',' or '}' is not found.", error_code::JSON_PARSE_ERROR); } if(*first == '}') { break; } ++first; first = skipWs(first, last); checkEof(first, last); } } ++first; return std::make_pair(dict, first); } } // namespace namespace { std::pair, std::string::const_iterator> decode (std::string::const_iterator first, std::string::const_iterator last, size_t depth) { first = skipWs(first, last); if(first == last) { throw DL_ABORT_EX2("JSON decoding failed:" " Unexpected EOF in term context.", error_code::JSON_PARSE_ERROR); } if(*first == '[') { return decodeArray(first, last, depth+1); } else if(*first == '{') { return decodeObject(first, last, depth+1); } else if(*first == '"') { return decodeString(first, last); } else if(*first == '-' || in(*first, '0', '9')) { return decodeNumber(first, last); } else if(*first == 't') { return decodeTrue(first, last); } else if(*first == 'f') { return decodeFalse(first, last); } else if(*first == 'n') { return decodeNull(first, last); } else { throw DL_ABORT_EX2("JSON decoding failed:" " Unexpected character in term context.", error_code::JSON_PARSE_ERROR); } } } // namespace SharedHandle decode(const std::string& json) { std::string::const_iterator first = json.begin(); std::string::const_iterator last = json.end(); first = skipWs(first, last); if(first == last) { throw DL_ABORT_EX2("JSON decoding failed:" " Unexpected EOF in term context.", error_code::JSON_PARSE_ERROR); } std::pair, std::string::const_iterator> r; if(*first == '[') { r = decodeArray(first, last, 1); } else if(*first == '{') { r = decodeObject(first, last, 1); } else { throw DL_ABORT_EX2("JSON decoding failed:" " Unexpected EOF in term context.", error_code::JSON_PARSE_ERROR); } return r.first; } std::string jsonEscape(const std::string& s) { std::string t; for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) { if(*i == '"' || *i == '\\' || *i == '/') { t += '\\'; t += *i; } else if(*i == '\b') { t += "\\b"; } else if(*i == '\f') { t += "\\f"; } else if(*i == '\n') { t += "\\n"; } else if(*i == '\r') { t += "\\r"; } else if(*i == '\t') { t += "\\t"; } else if(in(static_cast(*i), 0x00u, 0x1Fu)) { t += "\\u00"; char temp[3]; temp[2] = '\0'; temp[0] = (*i >> 4); temp[1] = (*i)&0x0Fu; for(int j = 0; j < 2; ++j) { if(temp[j] < 10) { temp[j] += '0'; } else { temp[j] += 'A'-10; } } t += temp; } else { t += *i; } } return t; } std::string encode(const ValueBase* vlb) { class JsonValueBaseVisitor:public ValueBaseVisitor { private: std::ostringstream out_; public: virtual void visit(const String& string) { const std::string& s = string.s(); std::string t = jsonEscape(s); out_ << '"'; out_.write(t.data(), t.size()); out_ << '"'; } virtual void visit(const Integer& integer) { out_ << integer.i(); } virtual void visit(const Bool& boolValue) { out_ << (boolValue.val() ? "true" : "false"); } virtual void visit(const Null& nullValue) { out_ << "null"; } virtual void visit(const List& list) { out_ << '['; List::ValueType::const_iterator i = list.begin(); if(!list.empty()) { (*i)->accept(*this); } ++i; for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){ out_ << ','; (*i)->accept(*this); } out_ << ']'; } virtual void visit(const Dict& dict) { out_ << '{'; Dict::ValueType::const_iterator i = dict.begin(); if(!dict.empty()) { std::string key = jsonEscape((*i).first); out_ << '"'; out_.write(key.data(), key.size()); out_ << "\":"; (*i).second->accept(*this); } ++i; for(Dict::ValueType::const_iterator eoi = dict.end(); i != eoi; ++i){ out_ << ','; std::string key = jsonEscape((*i).first); out_ << '"'; out_.write(key.data(), key.size()); out_ << "\":"; (*i).second->accept(*this); } out_ << '}'; } std::string getResult() const { return out_.str(); } }; JsonValueBaseVisitor visitor; vlb->accept(visitor); return visitor.getResult(); } // Serializes JSON object or array. std::string encode(const SharedHandle& json) { std::ostringstream out; return encode(out, json.get()).str(); } JsonGetParam::JsonGetParam (const std::string& request, const std::string& callback) : request(request), callback(callback) {} JsonGetParam decodeGetParams(const std::string& query) { std::string jsonRequest; std::string callback; if(!query.empty() && query[0] == '?') { std::string method; std::string id; std::string params; std::vector getParams; util::split(query.substr(1), std::back_inserter(getParams), "&"); for(std::vector::const_iterator i = getParams.begin(), eoi = getParams.end(); i != eoi; ++i) { if(util::startsWith(*i, "method=")) { method = (*i).substr(7); } else if(util::startsWith(*i, "id=")) { id = (*i).substr(3); } else if(util::startsWith(*i, "params=")) { params = (*i).substr(7); } else if(util::startsWith(*i, "jsoncallback=")) { callback = (*i).substr(13); } } std::string jsonParam = Base64::decode(util::percentDecode(params)); if(method.empty() && id.empty()) { // Assume batch call. jsonRequest = jsonParam; } else { jsonRequest = '{'; if(!method.empty()) { strappend(jsonRequest, "\"method\":\"", method, "\""); } if(!id.empty()) { strappend(jsonRequest, ",\"id\":\"", id, "\""); } if(!params.empty()) { strappend(jsonRequest, ",\"params\":", jsonParam); } jsonRequest += '}'; } } return JsonGetParam(jsonRequest, callback); } } // namespace json } // namespace aria2