Newer
Older
XinYang_IOS / Pods / OpenVPNAdapter / Sources / OpenVPN3 / openvpn / common / jsonhelper.hpp
@zhangfeng zhangfeng on 7 Dec 2023 24 KB 1.8.0
//    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;
      }
    }
  }
}