aria2/src/json.cc

664 lines
18 KiB
C++

/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2011 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#include "json.h"
#include <sstream>
#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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<char>(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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, std::string::const_iterator>
decodeArray
(std::string::const_iterator first,
std::string::const_iterator last,
size_t depth)
{
checkDepth(depth);
SharedHandle<List> list = List::g();
// Consume first char, assuming it is '['.
++first;
first = skipWs(first, last);
checkEof(first, last);
if(*first != ']') {
while(1) {
std::pair<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, std::string::const_iterator>
decodeObject
(std::string::const_iterator first,
std::string::const_iterator last,
size_t depth)
{
checkDepth(depth);
SharedHandle<Dict> dict = Dict::g();
// Consume first char, assuming it is '{'
++first;
first = skipWs(first, last);
checkEof(first, last);
if(*first != '}') {
while(1) {
std::pair<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, std::string::const_iterator>
valueRet = decode(first, last, depth);
dict->put(downcast<String>(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<SharedHandle<ValueBase>, 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<ValueBase> 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<SharedHandle<ValueBase>, 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<unsigned char>(*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<ValueBase>& 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<std::string> getParams;
util::split(query.substr(1), std::back_inserter(getParams), "&");
for(std::vector<std::string>::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