Files
OpenVPNAdapter/Sources/OpenVPN3/openvpn/common/jsonhelper.hpp

824 lines
24 KiB
C++

// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2020 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <cstring>
#include <cstdint>
#include <utility>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/number.hpp>
#include <openvpn/common/file.hpp>
#include <openvpn/common/jsonlib.hpp>
#include <openvpn/common/jsonhelperfmt.hpp>
#include <openvpn/buffer/bufstr.hpp>
#include <openvpn/buffer/bufstream.hpp>
#ifndef HAVE_JSON
#error no JSON library available
#endif
namespace openvpn {
namespace json {
OPENVPN_EXCEPTION(json_parse);
template <typename TITLE>
inline Json::Value parse(const std::string& str, const TITLE& title)
{
#ifdef OPENVPN_JSON
return Json::Value::parse(str, StringTempl::to_string(title));
#else
Json::CharReaderBuilder builder;
builder["collectComments"] = false;
Json::Value root;
std::string errors;
std::istringstream instr(str);
if (!Json::parseFromStream(builder, instr, &root, &errors))
throw json_parse(StringTempl::to_string(title) + " : " + errors);
return root;
#endif
}
inline Json::Value parse(const std::string& str)
{
return parse(str, "json");
}
inline Json::Value parse_from_file(const std::string& fn)
{
return parse(read_text_utf8(fn), fn);
}
template <typename BUFFER, typename TITLE>
inline Json::Value parse_from_buffer(const BUFFER& buf, const TITLE& title)
{
#ifdef OPENVPN_JSON
return Json::Value::parse(buf, StringTempl::to_string(title));
#else
Json::CharReaderBuilder builder;
builder["collectComments"] = false;
Json::Value root;
std::string errors;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
if (!reader->parse(reinterpret_cast<const char *>(buf.c_data()), reinterpret_cast<const char *>(buf.c_data()) + buf.size(), &root, &errors))
throw json_parse(StringTempl::to_string(title) + " : " + errors);
return root;
#endif
}
#ifdef OPENVPN_JSON_INTERNAL
template <typename NAME, typename TITLE>
inline const Json::Value& cast(const Json::ValueType target_type,
const Json::Value& value,
const NAME& name,
const bool optional,
const TITLE& title)
{
if (value.isNull())
{
if (optional)
return value;
throw json_parse(Json::Value::type_string(target_type) + " cast " + fmt_name(name, title) + " is null");
}
if (!value.isConvertibleTo(target_type))
throw json_parse(Json::Value::type_string(target_type) + " cast " + fmt_name(name, title) + " is of incorrect type (" + value.type_string() + ')');
return value;
}
template <typename NAME, typename TITLE>
inline const Json::Value& cast(const Json::ValueType target_type,
const Json::Value& value,
const NAME& name,
const bool optional)
{
return cast(target_type, value, name, optional, nullptr);
}
#endif
template <typename T, typename NAME>
inline void from_vector(Json::Value& root, const T& vec, const NAME& name)
{
Json::Value array(Json::arrayValue);
for (auto &e : vec)
array.append(e.to_json());
if (array.size())
root[name] = array;
}
template <typename TITLE>
inline void assert_dict(const Json::Value& obj, const TITLE& title)
{
if (!obj.isObject())
throw json_parse(fmt_name_cast(title) + " is not a JSON dictionary");
}
template <typename TITLE>
inline bool is_dict(const Json::Value& obj, const TITLE& title)
{
if (obj.isNull())
return false;
assert_dict(obj, title);
return true;
}
template <typename NAME>
inline bool exists(const Json::Value& root, const NAME& name)
{
if (!root.isObject())
return false;
return !root[name].isNull();
}
template <typename NAME>
inline bool string_exists(const Json::Value& root, const NAME& name)
{
if (!root.isObject())
return false;
return root[name].isString();
}
template <typename T, typename NAME, typename TITLE>
inline void to_vector(const Json::Value& root, T& vec,
const NAME& name,
const TITLE& title)
{
const Json::Value& array = root[name];
if (array.isNull())
return;
if (!array.isArray())
throw json_parse("array " + fmt_name(name, title) + " is of incorrect type");
for (unsigned int i = 0; i < array.size(); ++i)
{
vec.emplace_back();
vec.back().from_json(array[i], fmt_name(name, title));
}
}
template <typename NAME, typename TITLE>
inline std::string get_string(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("string " + fmt_name(name, title) + " is missing");
if (!value.isString())
throw json_parse("string " + fmt_name(name, title) + " is of incorrect type");
return value.asString();
}
template <typename NAME>
inline std::string get_string(const Json::Value& root, const NAME& name)
{
return get_string(root, name, nullptr);
}
#ifdef OPENVPN_JSON_INTERNAL
template <typename NAME, typename TITLE>
inline const std::string& get_string_ref(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("string " + fmt_name(name, title) + " is missing");
if (!value.isString())
throw json_parse("string " + fmt_name(name, title) + " is of incorrect type");
return value.asStringRef();
}
template <typename NAME>
inline const std::string& get_string_ref(const Json::Value& root, const NAME& name)
{
return get_string_ref(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline const std::string* get_string_ptr(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return nullptr;
if (!value.isString())
throw json_parse("string " + fmt_name(name, title) + " is of incorrect type");
return value.asStringPtr();
}
template <typename NAME>
inline const std::string* get_string_ptr(const Json::Value& root, const NAME& name)
{
return get_string_ptr(root, name, nullptr);
}
#endif
template <typename NAME, typename TITLE>
inline std::string get_string_optional(const Json::Value& root,
const NAME& name,
const std::string& default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isString())
throw json_parse("string " + fmt_name(name, title) + " is of incorrect type");
return value.asString();
}
template <typename NAME>
inline std::string get_string_optional(const Json::Value& root,
const NAME& name,
const std::string& default_value)
{
return get_string_optional(root, name, default_value, nullptr);
}
template <typename TITLE>
inline std::string get_string_from_array(const Json::Value& root,
const Json::ArrayIndex index,
const TITLE& title)
{
const Json::Value& value = root[index];
if (value.isNull())
throw json_parse("string " + fmt_name(index, title) + " is missing");
if (!value.isString())
throw json_parse("string " + fmt_name(index, title) + " is of incorrect type");
return value.asString();
}
inline std::string get_string_from_array(const Json::Value& root,
const Json::ArrayIndex index)
{
return get_string_from_array(root, index, nullptr);
}
template <typename NAME, typename TITLE>
inline int get_int(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("int " + fmt_name(name, title) + " is missing");
if (!value.isInt())
throw json_parse("int " + fmt_name(name, title) + " is of incorrect type");
return value.asInt();
}
template <typename NAME>
inline int get_int(const Json::Value& root, const NAME& name)
{
return get_int(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline int get_int_optional(const Json::Value& root,
const NAME& name,
const int default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isInt())
throw json_parse("int " + fmt_name(name, title) + " is of incorrect type");
return value.asInt();
}
template <typename NAME>
inline int get_int_optional(const Json::Value& root,
const NAME& name,
const int default_value)
{
return get_int_optional(root, name, default_value, nullptr);
}
template <typename NAME, typename TITLE>
inline unsigned int get_uint(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("uint " + fmt_name(name, title) + " is missing");
if (!value.isUInt())
throw json_parse("uint " + fmt_name(name, title) + " is of incorrect type");
return value.asUInt();
}
template <typename NAME>
inline unsigned int get_uint(const Json::Value& root, const NAME& name)
{
return get_uint(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline unsigned int get_uint_optional(const Json::Value& root,
const NAME& name,
const unsigned int default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isUInt())
throw json_parse("uint " + fmt_name(name, title) + " is of incorrect type");
return value.asUInt();
}
template <typename NAME>
inline unsigned int get_uint_optional(const Json::Value& root,
const NAME& name,
const unsigned int default_value)
{
return get_uint_optional(root, name, default_value, nullptr);
}
template <typename NAME, typename TITLE>
inline unsigned int get_uint_via_string(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("uint-via-string " + fmt_name(name, title) + " is missing");
if (!value.isString())
throw json_parse("uint-via-string " + fmt_name(name, title) + " is of incorrect type");
unsigned int ret;
if (!parse_number(value.asString(), ret))
throw json_parse("uint-via-string " + fmt_name(name, title) + " failed to parse");
return ret;
}
template <typename NAME>
inline unsigned int get_uint_via_string(const Json::Value& root,
const NAME& name)
{
return get_uint_via_string(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline unsigned int get_uint_optional_via_string(const Json::Value& root,
const NAME& name,
const unsigned int default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isString())
throw json_parse("uint-via-string " + fmt_name(name, title) + " is of incorrect type");
unsigned int ret;
if (!parse_number(value.asString(), ret))
throw json_parse("uint-via-string " + fmt_name(name, title) + " failed to parse");
return ret;
}
template <typename NAME>
inline unsigned int get_uint_optional_via_string(const Json::Value& root,
const NAME& name,
const unsigned int default_value)
{
return get_uint_optional_via_string(root, name, default_value, nullptr);
}
template <typename NAME, typename TITLE>
inline std::uint64_t get_uint64(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("uint64 " + fmt_name(name, title) + " is missing");
if (!value.isUInt64())
throw json_parse("uint64 " + fmt_name(name, title) + " is of incorrect type");
return value.asUInt64();
}
template <typename NAME>
inline std::uint64_t get_uint64(const Json::Value& root, const NAME& name)
{
return get_uint64(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline std::uint64_t get_uint64_optional(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isUInt64())
throw json_parse("uint64 " + fmt_name(name, title) + " is of incorrect type");
return value.asUInt64();
}
template <typename NAME, typename TITLE>
inline std::int64_t get_int64_optional(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isInt64())
throw json_parse("int64 " + fmt_name(name, title) + " is of incorrect type");
return value.asInt64();
}
template <typename NAME>
inline std::uint64_t get_uint64_optional(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value)
{
return get_uint64_optional(root, name, default_value, nullptr);
}
/*
* The get_integer_optional function are used to select the right
* method based on the default_value parameter
*/
template<typename NAME, typename TITLE>
std::uint64_t get_integer_optional(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value,
const TITLE& title)
{
return get_uint64_optional(root, name, default_value, title);
}
template<typename NAME, typename TITLE>
std::int64_t get_integer_optional(const Json::Value& root,
const NAME& name,
const std::int64_t default_value,
const TITLE& title)
{
return get_int64_optional(root, name, default_value, title);
}
template<typename NAME, typename TITLE>
inline unsigned int get_integer_optional(const Json::Value& root,
const NAME& name,
const unsigned int default_value,
const TITLE& title)
{
return get_uint_optional(root, name, default_value, title);
}
template<typename NAME, typename TITLE>
inline int get_integer_optional(const Json::Value& root,
const NAME& name,
const int default_value,
const TITLE& title)
{
return get_int_optional(root, name, default_value, title);
}
template <typename NAME, typename TITLE>
inline std::uint64_t get_uint64_via_string(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("uint64-via-string " + fmt_name(name, title) + " is missing");
if (!value.isString())
throw json_parse("uint64-via-string " + fmt_name(name, title) + " is of incorrect type");
std::uint64_t ret;
if (!parse_number(value.asString(), ret))
throw json_parse("uint64-via-string " + fmt_name(name, title) + " failed to parse");
return ret;
}
template <typename NAME>
inline std::uint64_t get_uint64_via_string(const Json::Value& root,
const NAME& name)
{
return get_uint64_via_string(root, name, nullptr);
}
template <typename NAME, typename TITLE>
inline std::uint64_t get_uint64_optional_via_string(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
return default_value;
if (!value.isString())
throw json_parse("uint64-via-string " + fmt_name(name, title) + " is of incorrect type");
std::uint64_t ret;
if (!parse_number(value.asString(), ret))
throw json_parse("uint64-via-string " + fmt_name(name, title) + " failed to parse");
return ret;
}
template <typename NAME>
inline std::uint64_t get_uint64_optional_via_string(const Json::Value& root,
const NAME& name,
const std::uint64_t default_value)
{
return get_uint64_optional_via_string(root, name, default_value, nullptr);
}
template <typename NAME, typename TITLE>
inline bool get_bool(const Json::Value& root,
const NAME& name,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
throw json_parse("bool " + fmt_name(name, title) + " is missing");
if (!value.isBool())
throw json_parse("bool " + fmt_name(name, title) + " is of incorrect type");
return value.asBool();
}
template <typename NAME>
inline bool get_bool(const Json::Value& root, const NAME& name)
{
return get_bool(root, name, nullptr);
}
template <typename NAME>
inline bool get_bool_optional(const Json::Value& root,
const NAME& name,
const bool default_value=false)
{
const Json::Value& jv = root[name];
if (jv.isConvertibleTo(Json::booleanValue))
return jv.asBool();
else
return default_value;
}
template <typename NAME>
inline int get_bool_tristate(const Json::Value& root,
const NAME& name)
{
const Json::Value& jv = root[name];
if (jv.isConvertibleTo(Json::booleanValue))
return jv.asBool() ? 1 : 0;
else
return -1;
}
template <typename NAME, typename TITLE>
inline const Json::Value& get_dict(const Json::Value& root,
const NAME& name,
const bool optional,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
{
if (optional)
return value;
throw json_parse("dictionary " + fmt_name(name, title) + " is missing");
}
if (!value.isObject())
throw json_parse("dictionary " + fmt_name(name, title) + " is of incorrect type");
return value;
}
template <typename NAME>
inline const Json::Value& get_dict(const Json::Value& root,
const NAME& name,
const bool optional)
{
return get_dict(root, name, optional, nullptr);
}
template <typename TITLE>
inline const Json::Value& cast_dict(const Json::Value& value,
const bool optional,
const TITLE& title)
{
if (value.isNull())
{
if (optional)
return value;
throw json_parse("dictionary cast " + fmt_name_cast(title) + " is null");
}
if (!value.isObject())
throw json_parse("dictionary cast " + fmt_name_cast(title) + " is of incorrect type");
return value;
}
inline const Json::Value& cast_dict(const Json::Value& value,
const bool optional)
{
return cast_dict(value, optional, nullptr);
}
template <typename NAME, typename TITLE>
inline const Json::Value& get_array(const Json::Value& root,
const NAME& name,
const bool optional,
const TITLE& title)
{
const Json::Value& value = root[name];
if (value.isNull())
{
if (optional)
return value;
throw json_parse("array " + fmt_name(name, title) + " is missing");
}
if (!value.isArray())
throw json_parse("array " + fmt_name(name, title) + " is of incorrect type");
return value;
}
template <typename NAME>
inline const Json::Value& get_array(const Json::Value& root,
const NAME& name,
const bool optional)
{
return get_array(root, name, optional, nullptr);
}
template <typename TITLE>
inline const Json::Value& cast_array(const Json::Value& value,
const bool optional,
const TITLE& title)
{
if (value.isNull())
{
if (optional)
return value;
throw json_parse("array cast " + fmt_name_cast(title) + " is null");
}
if (!value.isArray())
throw json_parse("array cast " + fmt_name_cast(title) + " is of incorrect type");
return value;
}
inline const Json::Value& cast_array(const Json::Value& value,
const bool optional)
{
return cast_array(value, optional, nullptr);
}
template <typename NAME, typename TITLE>
inline void to_string(const Json::Value& root,
std::string& dest,
const NAME& name,
const TITLE& title)
{
dest = get_string(root, name, title);
}
template <typename NAME, typename TITLE>
inline void to_string_optional(const Json::Value& root,
std::string& dest,
const NAME& name,
const std::string& default_value,
const TITLE& title)
{
dest = get_string_optional(root, name, default_value, title);
}
template <typename NAME, typename TITLE>
inline void to_int(const Json::Value& root,
int& dest,
const NAME& name,
const TITLE& title)
{
dest = get_int(root, name, title);
}
template <typename NAME, typename TITLE>
inline void to_uint(const Json::Value& root,
unsigned int& dest,
const NAME& name,
const TITLE& title)
{
dest = get_uint(root, name, title);
}
template <typename NAME, typename TITLE>
inline void to_uint_optional(const Json::Value& root,
unsigned int& dest,
const NAME& name,
const unsigned int default_value,
const TITLE& title)
{
dest = get_uint_optional(root, name, default_value, title);
}
template <typename NAME, typename TITLE>
inline void to_uint64(const Json::Value& root,
std::uint64_t& dest,
const NAME& name,
const TITLE& title)
{
dest = get_uint64(root, name, title);
}
template <typename NAME, typename TITLE>
inline void to_bool(const Json::Value& root,
bool& dest,
const NAME& name,
const TITLE& title)
{
dest = get_bool(root, name, title);
}
inline void format_compact(const Json::Value& root, Buffer& buf)
{
#ifdef OPENVPN_JSON
root.toCompactString(buf);
#else
Json::StreamWriterBuilder json_builder;
json_builder.settings_["indentation"] = "";
BufferStreamOut os(buf);
std::unique_ptr<Json::StreamWriter> sw(json_builder.newStreamWriter());
sw->write(root, &os);
#endif
}
inline std::string format_compact(const Json::Value& root,
const size_t size_hint=256)
{
BufferPtr bp = new BufferAllocated(size_hint, BufferAllocated::GROW);
format_compact(root, *bp);
return buf_to_string(*bp);
}
inline void format(const Json::Value& root, Buffer& buf)
{
#ifdef OPENVPN_JSON
root.toStyledString(buf);
#else
Json::StreamWriterBuilder json_builder;
json_builder.settings_["indentation"] = " ";
BufferStreamOut os(buf);
std::unique_ptr<Json::StreamWriter> sw(json_builder.newStreamWriter());
sw->write(root, &os);
#endif
}
inline std::string format(const Json::Value& root)
{
return root.toStyledString();
}
inline std::string error(const Json::Value& root)
{
const Json::Value& je = root["error"];
if (je.isString())
return je.asString();
else
return std::string();
}
// Guarantee that json object jr is a dictionary.
// Do this by encapsulating jr in a dictionary
// { "result": jr } if it is not already one.
inline Json::Value dict_result(Json::Value jr)
{
if (jr.isObject())
return jr;
else {
Json::Value jret(Json::objectValue);
jret["result"] = std::move(jr);
return jret;
}
}
}
}