Mercurial > irccd
changeset 641:7e2d0739f37c
Irccd: import json_util from code
Change how parsing values from JSON is done, the json_util functions do not
throw but return a boost::optional instead. Then, create some convenient local
wrappers in server_util.cpp to throw on missing values.
While here, add as much as possible const qualifier on variables that will not
be modified.
line wrap: on
line diff
--- a/irccdctl/cli.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/irccdctl/cli.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -36,9 +36,9 @@ if (code) throw boost::system::system_error(code); - auto c = json_util::to_string(message["command"]); + const auto c = json_util::get_string(message, "/command"_json_pointer); - if (c != req["command"].get<std::string>()) { + if (!c) { recv_response(ctl, std::move(req), std::move(handler)); return; }
--- a/irccdctl/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/irccdctl/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -85,7 +85,7 @@ #endif // Global options. -bool verbose = true; +bool verbose = false; // Connection to instance. std::unique_ptr<connection> conn; @@ -508,11 +508,17 @@ if (code) throw boost::system::system_error(code); - if (verbose) - std::cout << string_util::sprintf("connected to irccd %d.%d.%d\n", - json_util::to_int(info["major"]), - json_util::to_int(info["minor"]), - json_util::to_int(info["patch"])); + if (verbose) { + const auto major = json_util::get_int(info, "/major"_json_pointer); + const auto minor = json_util::get_int(info, "/minor"_json_pointer); + const auto patch = json_util::get_int(info, "/patch"_json_pointer); + + if (!major || !minor || !patch) + std::cout << "connected to irccd (unknown version)" << std::endl; + else + std::cout << string_util::sprintf("connected to irccd %d.%d.%d\n", + *major, *minor, *patch); + } connected = true; });
--- a/irccdctl/plugin_info_cli.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/irccdctl/plugin_info_cli.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -36,10 +36,14 @@ request(ctl, {{ "plugin", args[0] }}, [] (auto result) { std::cout << std::boolalpha; - std::cout << "Author : " << json_util::get_string(result, "author") << std::endl; - std::cout << "License : " << json_util::get_string(result, "license") << std::endl; - std::cout << "Summary : " << json_util::get_string(result, "summary") << std::endl; - std::cout << "Version : " << json_util::get_string(result, "version") << std::endl; + std::cout << "Author : " << + json_util::get_string(result, "/author"_json_pointer).value_or("(unknown)") << std::endl; + std::cout << "License : " << + json_util::get_string(result, "/license"_json_pointer).value_or("(unknown)") << std::endl; + std::cout << "Summary : " << + json_util::get_string(result, "/summary"_json_pointer).value_or("(unknown)") << std::endl; + std::cout << "Version : " << + json_util::get_string(result, "/version"_json_pointer).value_or("(unknown)") << std::endl; }); }
--- a/irccdctl/watch_cli.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/irccdctl/watch_cli.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -52,111 +52,111 @@ void onConnect(const nlohmann::json &v) { std::cout << "event: onConnect\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; } void onInvite(const nlohmann::json &v) { std::cout << "event: onInvite\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; } void onJoin(const nlohmann::json &v) { std::cout << "event: onJoin\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; } void onKick(const nlohmann::json &v) { std::cout << "event: onKick\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; - std::cout << "target: " << json_util::pretty(v, "target") << "\n"; - std::cout << "reason: " << json_util::pretty(v, "reason") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; + std::cout << "target: " << json_util::pretty(v.value("target", "(unknown)")) << "\n"; + std::cout << "reason: " << json_util::pretty(v.value("reason", "(unknown)")) << "\n"; } void onMessage(const nlohmann::json &v) { std::cout << "event: onMessage\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; - std::cout << "message: " << json_util::pretty(v, "message") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; + std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; } void onMe(const nlohmann::json &v) { std::cout << "event: onMe\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "target: " << json_util::pretty(v, "target") << "\n"; - std::cout << "message: " << json_util::pretty(v, "message") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "target: " << json_util::pretty(v.value("target", "(unknown)")) << "\n"; + std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; } void onMode(const nlohmann::json &v) { std::cout << "event: onMode\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "mode: " << json_util::pretty(v, "mode") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "mode: " << json_util::pretty(v.value("mode", "(unknown)")) << "\n"; } void onNames(const nlohmann::json &v) { std::cout << "event: onNames\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; - std::cout << "names: " << json_util::pretty(v, "names") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; + std::cout << "names: " << json_util::pretty(v.value("names", "(unknown)")) << "\n"; } void onNick(const nlohmann::json &v) { std::cout << "event: onNick\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "nickname: " << json_util::pretty(v, "nickname") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "nickname: " << json_util::pretty(v.value("nickname", "(unknown)")) << "\n"; } void onNotice(const nlohmann::json &v) { std::cout << "event: onNotice\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "message: " << json_util::pretty(v, "message") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; } void onPart(const nlohmann::json &v) { std::cout << "event: onPart\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; - std::cout << "reason: " << json_util::pretty(v, "reason") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; + std::cout << "reason: " << json_util::pretty(v.value("reason", "(unknown)")) << "\n"; } void onTopic(const nlohmann::json &v) { std::cout << "event: onTopic\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; - std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; - std::cout << "topic: " << json_util::pretty(v, "topic") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; + std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; + std::cout << "topic: " << json_util::pretty(v.value("topic", "(unknown)")) << "\n"; } void onWhois(const nlohmann::json &v) { std::cout << "event: onWhois\n"; - std::cout << "server: " << json_util::pretty(v, "server") << "\n"; - std::cout << "nickname: " << json_util::pretty(v, "nickname") << "\n"; - std::cout << "username: " << json_util::pretty(v, "username") << "\n"; - std::cout << "host: " << json_util::pretty(v, "host") << "\n"; - std::cout << "realname: " << json_util::pretty(v, "realname") << "\n"; + std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; + std::cout << "nickname: " << json_util::pretty(v.value("nickname", "(unknown)")) << "\n"; + std::cout << "username: " << json_util::pretty(v.value("username", "(unknown)")) << "\n"; + std::cout << "host: " << json_util::pretty(v.value("host", "(unknown)")) << "\n"; + std::cout << "realname: " << json_util::pretty(v.value("realname", "(unknown)")) << "\n"; } const std::unordered_map<std::string, std::function<void (const nlohmann::json&)>> events{ @@ -181,7 +181,8 @@ if (code) throw boost::system::system_error(code); - auto it = events.find(json_util::to_string(message["event"])); + const auto event = json_util::get_string(message, "/event"_json_pointer); + const auto it = events.find(event ? *event : ""); if (it != events.end()) { if (fmt == "json")
--- a/libcommon/irccd/json_util.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libcommon/irccd/json_util.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -1,7 +1,7 @@ /* * json_util.cpp -- utilities for JSON * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * Copyright (c) 2018 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,58 +23,136 @@ namespace json_util { -nlohmann::json require(const nlohmann::json& json, const std::string& key, nlohmann::json::value_t type) +boost::optional<nlohmann::json> get(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept { - auto it = json.find(key); - auto dummy = nlohmann::json(type); + // Unfortunately, there is no find using pointer yet. + try { + return json.at(key); + } catch (...) { + return boost::none; + } +} - if (it == json.end()) - throw std::runtime_error(string_util::sprintf("missing '%s' property", key)); - if (it->type() != type) - throw std::runtime_error(string_util::sprintf("invalid '%s' property (%s expected, got %s)", - key, it->type_name(), dummy.type_name())); +boost::optional<bool> get_bool(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept +{ + const auto v = get(json, key); - return *it; + if (!v || !v->is_boolean()) + return boost::none; + + return v->get<bool>(); } -std::string require_identifier(const nlohmann::json& json, const std::string& key) +boost::optional<std::uint64_t> get_int(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept { - auto id = require_string(json, key); + const auto v = get(json, key); + + if (!v || !v->is_number_integer()) + return boost::none; + + return v->get<std::uint64_t>(); +} + +boost::optional<std::uint64_t> get_uint(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept +{ + const auto v = get(json, key); - if (!string_util::is_identifier(id)) - throw std::runtime_error(string_util::sprintf("invalid '%s' identifier property", id)); + if (!v || !v->is_number_unsigned()) + return boost::none; + + return v->get<std::uint64_t>(); +} - return id; +boost::optional<std::string> get_string(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept +{ + const auto v = get(json, key); + + if (!v || !v->is_string()) + return boost::none; + + return v->get<std::string>(); } -std::int64_t require_int(const nlohmann::json& json, const std::string& key) +boost::optional<bool> optional_bool(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + bool def) noexcept { - auto it = json.find(key); + const auto v = get(json, key); + + if (!v) + return def; + if (!v->is_boolean()) + return boost::none; - if (it == json.end()) - throw std::runtime_error(string_util::sprintf("missing '%s' property", key)); - if (it->is_number_integer()) - return it->get<int>(); - if (it->is_number_unsigned() && it->get<unsigned>() <= INT_MAX) - return static_cast<int>(it->get<unsigned>()); + return v->get<bool>(); +} - throw std::runtime_error(string_util::sprintf("invalid '%s' property (%s expected, got %s)", - key, it->type_name(), nlohmann::json(0).type_name())); +boost::optional<std::int64_t> optional_int(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + std::int64_t def) noexcept +{ + const auto v = get(json, key); + + if (!v) + return def; + if (!v->is_number_integer()) + return boost::none; + + return v->get<std::int64_t>(); } -std::uint64_t require_uint(const nlohmann::json& json, const std::string& key) +boost::optional<std::uint64_t> optional_uint(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + std::uint64_t def) noexcept { - auto it = json.find(key); + const auto v = get(json, key); + + if (!v) + return def; + if (!v->is_number_unsigned()) + return boost::none; + + return v->get<std::uint64_t>(); +} + +boost::optional<std::string> optional_string(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + const std::string& def) noexcept +{ + const auto v = get(json, key); - if (it == json.end()) - throw std::runtime_error(string_util::sprintf("missing '%s' property", key)); - if (it->is_number_unsigned()) - return it->get<unsigned>(); - if (it->is_number_integer() && it->get<int>() >= 0) - return static_cast<unsigned>(it->get<int>()); + if (!v) + return def; + if (!v->is_string()) + return boost::none; + + return v->get<std::string>(); +} - throw std::runtime_error(string_util::sprintf("invalid '%s' property (%s expected, got %s)", - key, it->type_name(), nlohmann::json(0U).type_name())); +std::string pretty(const nlohmann::json& value) +{ + switch (value.type()) { + case nlohmann::json::value_t::boolean: + return value.get<bool>() ? "true" : "false"; + case nlohmann::json::value_t::string: + return value.get<std::string>(); + default: + return value.dump(); + } +} + +bool contains(const nlohmann::json& array, const nlohmann::json& value) noexcept +{ + for (const auto &v : array) + if (v == value) + return true; + + return false; } } // !json_util
--- a/libcommon/irccd/json_util.hpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libcommon/irccd/json_util.hpp Tue Mar 20 20:01:02 2018 +0100 @@ -1,7 +1,7 @@ /* * json_util.hpp -- utilities for JSON * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * Copyright (c) 2018 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,15 +19,20 @@ #ifndef IRCCD_COMMON_JSON_UTIL_HPP #define IRCCD_COMMON_JSON_UTIL_HPP -#include <climits> - -#include <json.hpp> - /** * \file json_util.hpp * \brief Utilities for JSON. */ +#include <irccd/sysconfig.hpp> + +#include <cstdint> +#include <string> + +#include <boost/optional.hpp> + +#include <json.hpp> + namespace irccd { /** @@ -36,196 +41,123 @@ namespace json_util { /** - * Require a property. + * Get a JSON value from the given object or array. * - * \param json the json value - * \param key the property name - * \param type the requested property type - * \return the value - * \throw std::runtime_error if the property is missing + * \param json the JSON object/array + * \param key the pointer to the object + * \return the value or boost::none if not found */ -nlohmann::json require(const nlohmann::json& json, const std::string& key, nlohmann::json::value_t type); +IRCCD_EXPORT +boost::optional<nlohmann::json> get(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept; /** - * Convenient access for booleans. - * - * \param json the json object - * \param key the property key - * \return the boolean - * \throw std::runtime_error if the property is missing or not a boolean - */ -inline bool require_bool(const nlohmann::json& json, const std::string& key) -{ - return require(json, key, nlohmann::json::value_t::boolean); -} - -/** - * Convenient access for unique identifiers. + * Get a bool or null if not found or invalid. * - * \param json the json object - * \param key the property key - * \return the identifier - * \throw std::runtime_error if the property is invalid + * \param json the JSON object/array + * \param key the pointer to the object + * \return the value or boost::none if not found or invalid */ -std::string require_identifier(const nlohmann::json& json, const std::string& key); - -/** - * Convenient access for ints. - * - * \param json the json object - * \param key the property key - * \return the int - * \throw std::runtime_error if the property is missing or not ant int - */ -std::int64_t require_int(const nlohmann::json& json, const std::string& key); +IRCCD_EXPORT +boost::optional<bool> get_bool(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept; /** - * Convenient access for unsigned ints. + * Get a 64 bit signed integer or null if not found or invalid. * - * \param json the json object - * \param key the property key - * \return the unsigned int - * \throw std::runtime_error if the property is missing or not ant int + * \param json the JSON object/array + * \param key the pointer to the object + * \return the value or boost::none if not found or invalid */ -std::uint64_t require_uint(const nlohmann::json& json, const std::string& key); - -/** - * Convenient access for strings. - * - * \param json the json object - * \param key the property key - * \return the string - * \throw std::runtime_error if the property is missing or not a string - */ -inline std::string require_string(const nlohmann::json& json, const std::string& key) -{ - return require(json, key, nlohmann::json::value_t::string); -} +IRCCD_EXPORT +boost::optional<std::uint64_t> get_int(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept; /** - * Convert the json value to boolean. + * Get a 64 bit unsigned integer or null if not found or invalid. * - * \param json the json value - * \param def the default value if not boolean - * \return a boolean + * \param json the JSON object/array + * \param key the pointer to the object + * \return the value or boost::none if not found or invalid */ -inline bool to_bool(const nlohmann::json& json, bool def = false) noexcept -{ - return json.is_boolean() ? json.get<bool>() : def; -} +IRCCD_EXPORT +boost::optional<std::uint64_t> get_uint(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept; /** - * Convert the json value to int. + * Get a string or null if not found or invalid. * - * \param json the json value - * \param def the default value if not an int - * \return an int + * \param json the JSON object/array + * \param key the pointer to the object + * \return the value or boost::none if not found or invalid */ -inline std::int64_t to_int(const nlohmann::json& json, std::int64_t def = 0) noexcept -{ - return json.is_number_integer() ? json.get<std::int64_t>() : def; -} +IRCCD_EXPORT +boost::optional<std::string> get_string(const nlohmann::json& json, + const nlohmann::json::json_pointer& key) noexcept; /** - * Convert the json value to unsigned. + * Get an optional bool. + * + * If the property is not found, return default value. If the property is not + * a bool, return boost::none, otherwise return the value. * - * \param json the json value - * \param def the default value if not a unsigned int - * \return an unsigned int - */ -inline std::uint64_t to_uint(const nlohmann::json& json, std::uint64_t def = 0) noexcept -{ - return json.is_number_unsigned() ? json.get<std::uint64_t>() : def; -} - -/** - * Convert the json value to string. - * - * \param json the json value - * \param def the default value if not a string - * \return a string + * \param json the JSON object/array + * \param key the pointer to the object + * \param def the default value + * \return the value, boost::none or def */ -inline std::string to_string(const nlohmann::json& json, std::string def = "") noexcept -{ - return json.is_string() ? json.get<std::string>() : def; -} - -/** - * Get a property or return null one if not found or if json is not an object. - * - * \param json the json value - * \param property the property key - * \return the value or null one if not found - */ -inline nlohmann::json get(const nlohmann::json& json, const std::string& property) noexcept -{ - auto it = json.find(property); - - if (it == json.end()) - return nlohmann::json(); - - return *it; -} +IRCCD_EXPORT +boost::optional<bool> optional_bool(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + bool def = false) noexcept; /** - * Convenient access for boolean with default value. + * Get an optional integer. + * + * If the property is not found, return default value. If the property is not + * an integer, return boost::none, otherwise return the value. * - * \param json the json value - * \param key the property key + * \param json the JSON object/array + * \param key the pointer to the object * \param def the default value - * \return the boolean + * \return the value, boost::none or def */ -inline bool get_bool(const nlohmann::json& json, - const std::string& key, - bool def = false) noexcept -{ - return to_bool(get(json, key), def); -} +IRCCD_EXPORT +boost::optional<std::int64_t> optional_int(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + std::int64_t def = 0) noexcept; /** - * Convenient access for ints with default value. + * Get an optional unsigned integer. + * + * If the property is not found, return default value. If the property is not + * an unsigned integer, return boost::none, otherwise return the value. * - * \param json the json value - * \param key the property key + * \param json the JSON object/array + * \param key the pointer to the object * \param def the default value - * \return the int + * \return the value, boost::none or def */ -inline std::int64_t get_int(const nlohmann::json& json, - const std::string& key, - std::int64_t def = 0) noexcept -{ - return to_int(get(json, key), def); -} +IRCCD_EXPORT +boost::optional<std::uint64_t> optional_uint(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + std::uint64_t def = 0) noexcept; /** - * Convenient access for unsigned ints with default value. + * Get an optional string. + * + * If the property is not found, return default value. If the property is not + * a string, return boost::none, otherwise return the value. * - * \param json the json value - * \param key the property key + * \param json the JSON object/array + * \param key the pointer to the object * \param def the default value - * \return the unsigned int + * \return the value, boost::none or def */ -inline std::uint64_t get_uint(const nlohmann::json& json, - const std::string& key, - std::uint64_t def = 0) noexcept -{ - return to_uint(get(json, key), def); -} - -/** - * Convenient access for strings with default value. - * - * \param json the json value - * \param key the property key - * \param def the default value - * \return the string - */ -inline std::string get_string(const nlohmann::json& json, - const std::string& key, - std::string def = "") noexcept -{ - return to_string(get(json, key), def); -} +IRCCD_EXPORT +boost::optional<std::string> optional_string(const nlohmann::json& json, + const nlohmann::json::json_pointer& key, + const std::string& def = "") noexcept; /** * Print the value as human readable. @@ -233,34 +165,8 @@ * \param value the value * \return the string */ -inline std::string pretty(const nlohmann::json& value) -{ - switch (value.type()) { - case nlohmann::json::value_t::boolean: - return value.get<bool>() ? "true" : "false"; - case nlohmann::json::value_t::string: - return value.get<std::string>(); - default: - return value.dump(); - } -} - -/** - * Pretty print a json value in the given object. - * - * \param object the object - * \param prop the property - * \return the pretty value or empty if key does not exist - */ -inline std::string pretty(const nlohmann::json& object, const std::string& prop) -{ - auto it = object.find(prop); - - if (it == object.end()) - return ""; - - return pretty(*it); -} +IRCCD_EXPORT +std::string pretty(const nlohmann::json& value); /** * Check if the array contains the given value. @@ -269,17 +175,11 @@ * \param value the JSON value to check * \return true if present */ -inline bool contains(const nlohmann::json& array, const nlohmann::json& value) -{ - for (const auto &v : array) - if (v == value) - return true; - - return false; -} +IRCCD_EXPORT +bool contains(const nlohmann::json& array, const nlohmann::json& value) noexcept; } // !json_util } // !irccd -#endif // !IRCCD_COMMON_JSON_UTIL_HPP +#endif // !JSON_UTIL_HPP
--- a/libirccd/irccd/daemon/command/plugin_config_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/plugin_config_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -80,7 +81,12 @@ void plugin_config_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto plugin = irccd.plugins().require(json_util::require_identifier(args, "plugin")); + const auto id = json_util::get_string(args, "/plugin"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + const auto plugin = irccd.plugins().require(*id); if (args.count("value") > 0) exec_set(client, *plugin, args);
--- a/libirccd/irccd/daemon/command/plugin_info_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/plugin_info_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -34,7 +35,12 @@ void plugin_info_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto plugin = irccd.plugins().require(json_util::require_identifier(args, "plugin")); + const auto id = json_util::get_string(args, "/plugin"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + auto plugin = irccd.plugins().require(*id); client.send({ { "command", "plugin-info" },
--- a/libirccd/irccd/daemon/command/plugin_load_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/plugin_load_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -34,7 +35,12 @@ void plugin_load_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - irccd.plugins().load(json_util::require_identifier(args, "plugin")); + const auto id = json_util::get_string(args, "/plugin"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + irccd.plugins().load(*id); client.success("plugin-load"); }
--- a/libirccd/irccd/daemon/command/plugin_reload_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/plugin_reload_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -34,7 +35,12 @@ void plugin_reload_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - irccd.plugins().reload(json_util::require_identifier(args, "plugin")); + const auto id = json_util::get_string(args, "/plugin"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + irccd.plugins().reload(*id); client.success("plugin-reload"); }
--- a/libirccd/irccd/daemon/command/plugin_unload_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/plugin_unload_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -34,7 +35,12 @@ void plugin_unload_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - irccd.plugins().unload(json_util::require_identifier(args, "plugin")); + const auto id = json_util::get_string(args, "/plugin"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + irccd.plugins().unload(*id); client.success("plugin-unload"); }
--- a/libirccd/irccd/daemon/command/rule_add_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/rule_add_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -34,13 +34,14 @@ void rule_add_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto index = json_util::get_uint(args, "index", irccd.rules().length()); - auto rule = rule_service::from_json(args); + auto index = json_util::get_uint(args, "/index"_json_pointer); + if (!index) + index = irccd.rules().length(); if (index > irccd.rules().length()) throw rule_error(rule_error::error::invalid_index); - irccd.rules().insert(rule, index); + irccd.rules().insert(rule_service::from_json(args), *index); client.success("rule-add"); }
--- a/libirccd/irccd/daemon/command/server_connect_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_connect_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -36,7 +36,7 @@ auto server = server_util::from_json(irccd.service(), args); if (irccd.servers().has(server->name())) - throw server_error(server_error::error::already_exists, server->name()); + throw server_error(server->name(), server_error::already_exists); irccd.servers().add(std::move(server)); client.success("server-connect");
--- a/libirccd/irccd/daemon/command/server_disconnect_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_disconnect_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <irccd/json_util.hpp> +#include <irccd/string_util.hpp> + #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> @@ -32,20 +35,17 @@ void server_disconnect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto it = args.find("server"); + const auto it = args.find("server"); if (it == args.end()) irccd.servers().clear(); else { - if (!it->is_string()) - throw server_error(server_error::invalid_identifier, ""); + if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) + throw server_error("", server_error::invalid_identifier); - auto name = it->get<std::string>(); - auto s = irccd.servers().get(name); + const auto name = it->get<std::string>(); - if (!s) - throw server_error(server_error::not_found, name); - + irccd.servers().require(name); irccd.servers().remove(name); }
--- a/libirccd/irccd/daemon/command/server_info_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_info_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -17,6 +17,7 @@ */ #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -32,8 +33,10 @@ void server_info_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + auto response = nlohmann::json::object(); - auto server = irccd.servers().require(args); // General stuff. response.push_back({"command", "server-info"});
--- a/libirccd/irccd/daemon/command/server_invite_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_invite_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,16 +35,17 @@ void server_invite_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto target = json_util::get_string(args, "target"); - auto channel = json_util::get_string(args, "channel"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto target = json_util::get_string(args, "/target"_json_pointer); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); - if (target.empty()) - throw server_error(server_error::invalid_nickname, server->name()); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!target || target->empty()) + throw server_error(server->name(), server_error::invalid_nickname); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->invite(target, channel); + server->invite(*target, *channel); client.success("server-invite"); }
--- a/libirccd/irccd/daemon/command/server_join_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_join_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -20,6 +20,7 @@ #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_join_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "channel"); - auto password = json_util::get_string(args, "password"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); + const auto password = json_util::get_string(args, "/password"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->join(channel, password); + server->join(*channel, password ? *password : ""); client.success("server-join"); }
--- a/libirccd/irccd/daemon/command/server_kick_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_kick_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -20,6 +20,7 @@ #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,17 +35,18 @@ void server_kick_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto target = json_util::get_string(args, "target"); - auto channel = json_util::get_string(args, "channel"); - auto reason = json_util::get_string(args, "reason"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto target = json_util::get_string(args, "/target"_json_pointer); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); + const auto reason = json_util::get_string(args, "/reason"_json_pointer); - if (target.empty()) - throw server_error(server_error::invalid_nickname, server->name()); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!target || target->empty()) + throw server_error(server->name(), server_error::invalid_nickname); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->kick(target, channel, reason); + server->kick(*target, *channel, reason ? *reason : ""); client.success("server-kick"); }
--- a/libirccd/irccd/daemon/command/server_me_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_me_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -20,6 +20,7 @@ #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/transport_client.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_me_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "target"); - auto message = json_util::get_string(args, "message"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/target"_json_pointer); + const auto message = json_util::get_string(args, "/message"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->me(channel, message); + server->me(*channel, message ? *message : ""); client.success("server-me"); }
--- a/libirccd/irccd/daemon/command/server_message_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_message_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_message_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "target"); - auto message = json_util::get_string(args, "message"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/target"_json_pointer); + const auto message = json_util::get_string(args, "/message"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->message(channel, message); + server->message(*channel, message ? *message : ""); client.success("server-message"); }
--- a/libirccd/irccd/daemon/command/server_mode_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_mode_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,20 +35,28 @@ void server_mode_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "channel"); - auto mode = json_util::get_string(args, "mode"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); + const auto mode = json_util::get_string(args, "/mode"_json_pointer); + + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); + if (!mode || mode->empty()) + throw server_error(server->name(), server_error::invalid_mode); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); - if (mode.empty()) - throw server_error(server_error::invalid_mode, server->name()); + auto limit = json_util::get_string(args, "/limit"_json_pointer); + auto user = json_util::get_string(args, "/user"_json_pointer); + auto mask = json_util::get_string(args, "/mask"_json_pointer); - auto limit = json_util::get_string(args, "limit"); - auto user = json_util::get_string(args, "user"); - auto mask = json_util::get_string(args, "mask"); + if (!limit) + limit = ""; + if (!user) + user = ""; + if (!mask) + mask = ""; - server->mode(channel, mode, limit, user, mask); + server->mode(*channel, *mode, *limit, *user, *mask); client.success("server-mode"); }
--- a/libirccd/irccd/daemon/command/server_nick_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_nick_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,13 +35,14 @@ void server_nick_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto nick = json_util::get_string(args, "nickname"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto nick = json_util::get_string(args, "/nickname"_json_pointer); - if (nick.empty()) - throw server_error(server_error::invalid_nickname, server->name()); + if (!nick || nick->empty()) + throw server_error(server->name(), server_error::invalid_nickname); - server->set_nickname(nick); + server->set_nickname(*nick); client.success("server-nick"); }
--- a/libirccd/irccd/daemon/command/server_notice_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_notice_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_notice_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "target"); - auto message = json_util::get_string(args, "message"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/target"_json_pointer); + const auto message = json_util::get_string(args, "/message"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->notice(channel, message); + server->notice(*channel, message ? *message : ""); client.success("server-notice"); }
--- a/libirccd/irccd/daemon/command/server_part_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_part_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_part_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "channel"); - auto reason = json_util::get_string(args, "reason"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); + const auto reason = json_util::get_string(args, "/reason"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->part(channel, reason); + server->part(*channel, reason ? *reason : ""); client.success("server-part"); }
--- a/libirccd/irccd/daemon/command/server_reconnect_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_reconnect_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <irccd/json_util.hpp> #include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> @@ -34,22 +35,16 @@ void server_reconnect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = args.find("server"); + const auto it = args.find("server"); - if (server == args.end()) { - for (auto& server : irccd.servers().servers()) + if (it == args.end()) { + for (const auto& server : irccd.servers().servers()) server->reconnect(); } else { - if (!server->is_string() || !string_util::is_identifier(server->get<std::string>())) - throw server_error(server_error::invalid_identifier, ""); + if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) + throw server_error("", server_error::invalid_identifier); - auto name = server->get<std::string>(); - auto s = irccd.servers().get(name); - - if (!s) - throw server_error(server_error::not_found, name); - - s->reconnect(); + irccd.servers().require(it->get<std::string>())->reconnect(); } client.success("server-reconnect");
--- a/libirccd/irccd/daemon/command/server_topic_command.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_topic_command.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_util.hpp> #include <irccd/daemon/transport_client.hpp> #include <irccd/daemon/service/server_service.hpp> @@ -34,14 +35,15 @@ void server_topic_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = irccd.servers().require(args); - auto channel = json_util::get_string(args, "channel"); - auto topic = json_util::get_string(args, "topic"); + const auto id = server_util::get_identifier(args); + const auto server = irccd.servers().require(id); + const auto channel = json_util::get_string(args, "/channel"_json_pointer); + const auto topic = json_util::get_string(args, "/topic"_json_pointer); - if (channel.empty()) - throw server_error(server_error::invalid_channel, server->name()); + if (!channel || channel->empty()) + throw server_error(server->name(), server_error::invalid_channel); - server->topic(channel, topic); + server->topic(*channel, topic ? *topic : ""); client.success("server-topic"); }
--- a/libirccd/irccd/daemon/plugin.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/plugin.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -87,6 +87,8 @@ switch (static_cast<plugin_error::error>(e)) { case plugin_error::not_found: return "plugin not found"; + case plugin_error::invalid_identifier: + return "invalid identifier"; case plugin_error::exec_error: return "plugin exec error"; case plugin_error::already_exists:
--- a/libirccd/irccd/daemon/plugin.hpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/plugin.hpp Tue Mar 20 20:01:02 2018 +0100 @@ -561,6 +561,9 @@ //!< No error. no_error = 0, + //!< The specified identifier is invalid. + invalid_identifier, + //!< The specified plugin is not found. not_found, @@ -584,7 +587,7 @@ * \param name the plugin name * \param message the optional message (e.g. error from plugin) */ - plugin_error(error code, std::string name, std::string message = "") noexcept; + plugin_error(error code, std::string name = "", std::string message = "") noexcept; /** * Get the plugin name.
--- a/libirccd/irccd/daemon/server.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/server.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -668,7 +668,7 @@ send(string_util::sprintf("WHOIS %s %s", target, target)); } -server_error::server_error(error code, std::string name) noexcept +server_error::server_error(std::string name, error code) noexcept : system_error(make_error_code(code)) , name_(std::move(name)) { @@ -708,8 +708,18 @@ return "invalid or empty mode"; case server_error::invalid_nickname: return "invalid nickname"; + case server_error::invalid_username: + return "invalid username"; + case server_error::invalid_realname: + return "invalid realname"; + case server_error::invalid_password: + return "invalid password"; case server_error::invalid_ping_timeout: return "invalid ping timeout"; + case server_error::invalid_ctcp_version: + return "invalid CTCP VERSION"; + case server_error::invalid_command_char: + return "invalid character command"; case server_error::ssl_disabled: return "ssl is not enabled"; default:
--- a/libirccd/irccd/daemon/server.hpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/server.hpp Tue Mar 20 20:01:02 2018 +0100 @@ -944,9 +944,24 @@ //!< The nickname was empty or invalid. invalid_nickname, + //!< The username was empty or invalid. + invalid_username, + + //!< The realname was empty or invalid. + invalid_realname, + + //!< Invalid password property. + invalid_password, + //!< Invalid ping timeout. invalid_ping_timeout, + //!< Invalid ctcp version. + invalid_ctcp_version, + + //!< Invalid command character. + invalid_command_char, + //!< SSL was requested but is disabled. ssl_disabled, }; @@ -958,10 +973,10 @@ /** * Constructor. * + * \param name the server name * \param code the error code - * \param name the server name */ - server_error(error code, std::string name) noexcept; + server_error(std::string name, error code) noexcept; /** * Get the server that triggered the error.
--- a/libirccd/irccd/daemon/server_util.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/server_util.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -25,84 +25,115 @@ #include "server_util.hpp" +using nlohmann::json; + +using std::uint16_t; +using std::string; +using std::forward; + namespace irccd { namespace server_util { namespace { -template <typename T> -T to_int(const std::string& value, const std::string& name, server_error::error errc) +template <typename... Args> +std::string require_conf_host(const ini::section& sc, Args&&... args) { - try { - return string_util::to_int<T>(value); - } catch (...) { - throw server_error(errc, name); - } + auto value = sc.get("host"); + + if (value.empty()) + throw server_error(forward<Args>(args)...); + + return value.value(); } -template <typename T> -T to_uint(const std::string& value, const std::string& name, server_error::error errc) -{ - try { - return string_util::to_uint<T>(value); - } catch (...) { - throw server_error(errc, name); - } -} - -template <typename T> -T to_uint(const nlohmann::json& value, const std::string& name, server_error::error errc) -{ - if (!value.is_number()) - throw server_error(errc, name); - - auto n = value.get<unsigned>(); - - if (n > std::numeric_limits<T>::max()) - throw server_error(errc, name); - - return static_cast<T>(n); -} - -std::string to_id(const ini::section& sc) +template <typename... Args> +std::string require_conf_id(const ini::section& sc, Args&&... args) { auto id = sc.get("name"); if (!string_util::is_identifier(id.value())) - throw server_error(server_error::invalid_identifier, ""); + throw server_error(forward<Args>(args)...); return id.value(); } -std::string to_id(const nlohmann::json& object) +template <typename Int, typename... Args> +Int optional_conf_int(const string& value, Args&&... args) { - auto id = json_util::get_string(object, "name"); + try { + return string_util::to_int<Int>(value); + } catch (...) { + throw server_error(std::forward<Args>(args)...); + } +} - if (!string_util::is_identifier(id)) - throw server_error(server_error::invalid_identifier, ""); +template <typename Int, typename... Args> +Int optional_conf_uint(const string& value, Args&&... args) +{ + try { + return string_util::to_uint<Int>(value); + } catch (...) { + throw server_error(std::forward<Args>(args)...); + } +} - return id; +template <typename Int, typename... Args> +Int optional_json_uint(const json& json, const json::json_pointer& key, Int def, Args&&... args) +{ + const auto v = json_util::optional_uint(json, key, def); + + if (!v || *v > std::numeric_limits<Int>::max()) + throw server_error(forward<Args>(args)...); + + return *v; } -std::string to_host(const ini::section& sc, const std::string& name) +bool optional_json_bool(const json& json, const json::json_pointer& key, bool def = false) { - auto value = sc.get("host"); + const auto v = json_util::optional_bool(json, key, def); - if (value.empty()) - throw server_error(server_error::invalid_hostname, name); + if (!v) + return def; - return value.value(); + return *v; } -std::string to_host(const nlohmann::json& object, const std::string& name) +template <typename... Args> +std::string optional_json_string(const json& json, + const json::json_pointer& key, + const string& def, + Args&&... args) { - auto value = json_util::get_string(object, "host"); + const auto v = json_util::optional_string(json, key, def); + + if (!v) + throw server_error(forward<Args>(args)...); + + return *v; +} - if (value.empty()) - throw server_error(server_error::invalid_hostname, name); +template <typename... Args> +std::string require_json_id(const nlohmann::json& json, Args&&... args) +{ + const auto id = json_util::get_string(json, "/name"_json_pointer); + + if (!id || !string_util::is_identifier(*id)) + throw server_error(forward<Args>(args)...); - return value; + return *id; +} + +template <typename... Args> +std::string require_json_host(const nlohmann::json& object, Args&&... args) +{ + const auto value = json_util::get_string(object, "/host"_json_pointer); + + if (!value || value->empty()) + throw server_error(forward<Args>(args)...); + + return *value; } void load_identity(server& server, const config& cfg, const std::string& identity) @@ -135,32 +166,39 @@ std::shared_ptr<server> from_json(boost::asio::io_service& service, const nlohmann::json& object) { - // TODO: move this function in server_service. - auto sv = std::make_shared<server>(service, to_id(object)); + const auto id = require_json_id(object, "", server_error::invalid_identifier); + const auto sv = std::make_shared<server>(service, id); // Mandatory fields. - sv->set_host(to_host(object, sv->name())); + sv->set_host(require_json_host(object, sv->name(), server_error::invalid_hostname)); // Optional fields. - if (object.count("port")) - sv->set_port(to_uint<std::uint16_t>(object["port"], sv->name(), server_error::invalid_port)); - sv->set_password(json_util::get_string(object, "password")); - sv->set_nickname(json_util::get_string(object, "nickname", sv->nickname())); - sv->set_realname(json_util::get_string(object, "realname", sv->realname())); - sv->set_username(json_util::get_string(object, "username", sv->username())); - sv->set_ctcp_version(json_util::get_string(object, "ctcpVersion", sv->ctcp_version())); - sv->set_command_char(json_util::get_string(object, "commandChar", sv->command_char())); + sv->set_port(optional_json_uint<uint16_t>(object, "/port"_json_pointer, sv->port(), + sv->name(), server_error::invalid_port)); + sv->set_password(optional_json_string(object, "/password"_json_pointer, sv->password(), + sv->name(), server_error::invalid_password)); + sv->set_nickname(optional_json_string(object, "/nickname"_json_pointer, sv->nickname(), + sv->name(), server_error::invalid_nickname)); + sv->set_realname(optional_json_string(object, "/realname"_json_pointer, sv->realname(), + sv->name(), server_error::invalid_realname)); + sv->set_username(optional_json_string(object, "/username"_json_pointer, sv->username(), + sv->name(), server_error::invalid_username)); + sv->set_ctcp_version(optional_json_string(object, "/ctcpVersion"_json_pointer, sv->ctcp_version(), + sv->name(), server_error::invalid_ctcp_version)); + sv->set_command_char(optional_json_string(object, "/commandChar"_json_pointer, sv->command_char(), + sv->name(), server_error::invalid_command_char)); - if (json_util::get_bool(object, "ipv6")) + // Boolean does not throw options though. + if (optional_json_bool(object, "/ipv6"_json_pointer)) sv->set_flags(sv->flags() | server::ipv6); - if (json_util::get_bool(object, "sslVerify")) + if (optional_json_bool(object, "/sslVerify"_json_pointer)) sv->set_flags(sv->flags() | server::ssl_verify); - if (json_util::get_bool(object, "autoRejoin")) + if (optional_json_bool(object, "/autoRejoin"_json_pointer)) sv->set_flags(sv->flags() | server::auto_rejoin); - if (json_util::get_bool(object, "joinInvite")) + if (optional_json_bool(object, "/joinInvite"_json_pointer)) sv->set_flags(sv->flags() | server::join_invite); - if (json_util::get_bool(object, "ssl")) + if (optional_json_bool(object, "/ssl"_json_pointer)) #if defined(HAVE_SSL) sv->set_flags(sv->flags() | server::ssl); #else @@ -176,10 +214,11 @@ { assert(sc.key() == "server"); - auto sv = std::make_shared<server>(service, to_id(sc)); + const auto id = require_conf_id(sc, "", server_error::invalid_identifier); + const auto sv = std::make_shared<server>(service, id); // Mandatory fields. - sv->set_host(to_host(sc, sv->name())); + sv->set_host(require_conf_host(sc, sv->name(), server_error::invalid_hostname)); // Optional fields. ini::section::const_iterator it; @@ -231,24 +270,36 @@ // Reconnect and ping timeout if ((it = sc.find("port")) != sc.end()) - sv->set_port(to_uint<std::uint16_t>(it->value(), + sv->set_port(optional_conf_uint<std::uint16_t>(it->value(), sv->name(), server_error::invalid_port)); if ((it = sc.find("reconnect-tries")) != sc.end()) - sv->set_reconnect_tries(to_int<std::int8_t>(it->value(), + sv->set_reconnect_tries(optional_conf_int<std::int8_t>(it->value(), sv->name(), server_error::invalid_reconnect_tries)); if ((it = sc.find("reconnect-timeout")) != sc.end()) - sv->set_reconnect_delay(to_uint<std::uint16_t>(it->value(), + sv->set_reconnect_delay(optional_conf_uint<std::uint16_t>(it->value(), sv->name(), server_error::invalid_reconnect_timeout)); if ((it = sc.find("ping-timeout")) != sc.end()) - sv->set_ping_timeout(to_uint<std::uint16_t>(it->value(), + sv->set_ping_timeout(optional_conf_uint<std::uint16_t>(it->value(), sv->name(), server_error::invalid_ping_timeout)); return sv; } +std::string get_identifier(const nlohmann::json& json) +{ + const auto v = json_util::get_string(json, "/server"_json_pointer); + + if (!v) + throw server_error("", server_error::invalid_identifier); + if (!string_util::is_identifier(*v)) + throw server_error(*v, server_error::invalid_identifier); + + return *v; +} + } // !server_util } // !irccd
--- a/libirccd/irccd/daemon/server_util.hpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/server_util.hpp Tue Mar 20 20:01:02 2018 +0100 @@ -71,6 +71,16 @@ const config& cfg, const ini::section& sc); +/** + * Get a server identifier from the JSON object. + * + * This searches for the `server` property. + * + * \param json the JSON object + * \throw server_error on errors + */ +std::string get_identifier(const nlohmann::json& json); + } // !server_util } // !irccd
--- a/libirccd/irccd/daemon/service/server_service.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/service/server_service.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -469,19 +469,17 @@ return *it; } -std::shared_ptr<server> server_service::require(const nlohmann::json& args, const std::string& key) +std::shared_ptr<server> server_service::require(const std::string& name) const { - auto id = json_util::get_string(args, key); - - if (!string_util::is_identifier(id)) - throw server_error(server_error::invalid_identifier, ""); + if (!string_util::is_identifier(name)) + throw server_error(name, server_error::invalid_identifier); - auto server = get(id); + const auto s = get(name); - if (!server) - throw server_error(server_error::not_found, id); + if (!s) + throw server_error(name, server_error::not_found); - return server; + return s; } void server_service::remove(const std::string& name)
--- a/libirccd/irccd/daemon/service/server_service.hpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/service/server_service.hpp Tue Mar 20 20:01:02 2018 +0100 @@ -102,12 +102,11 @@ /** * Find a server from a JSON object. * - * \pre json.is_object() - * \param json the JSON object - * \param key the server identifier property + * \param name the server name + * \return the server * \throw server_error on errors */ - std::shared_ptr<server> require(const nlohmann::json& json, const std::string& key = "server"); + std::shared_ptr<server> require(const std::string& name) const; /** * Remove a server from the irccd instance.
--- a/libirccd/irccd/daemon/service/transport_service.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/service/transport_service.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -20,6 +20,8 @@ #include <cassert> +#include <irccd/json_util.hpp> + #include <irccd/daemon/command.hpp> #include <irccd/daemon/ip_transport_server.hpp> #include <irccd/daemon/irccd.hpp> @@ -39,19 +41,19 @@ { assert(object.is_object()); - auto name = object.find("command"); + const auto name = json_util::get_string(object, "/command"_json_pointer); - if (name == object.end() || !name->is_string()) { + if (!name) { tc->error(irccd_error::invalid_message); return; } auto cmd = std::find_if(commands_.begin(), commands_.end(), [&] (const auto& cptr) { - return cptr->get_name() == name->template get<std::string>(); + return cptr->get_name() == *name; }); if (cmd == commands_.end()) - tc->error(irccd_error::invalid_command, name->get<std::string>()); + tc->error(irccd_error::invalid_command, *name); else { try { (*cmd)->exec(irccd_, *tc, object);
--- a/libirccd/irccd/daemon/transport_server.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccd/irccd/daemon/transport_server.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -38,13 +38,13 @@ return; } - auto command = json_util::to_string(message["command"]); - auto password = json_util::to_string(message["password"]); + const auto command = json_util::get_string(message, "/command"_json_pointer); + const auto password = json_util::get_string(message, "/password"_json_pointer); - if (command != "auth") { + if (!command || *command != "auth") { client->error(irccd_error::auth_required); code = irccd_error::auth_required; - } else if (password != password_) { + } else if (!password || *password != password_) { client->error(irccd_error::invalid_auth); code = irccd_error::invalid_auth; } else {
--- a/libirccdctl/irccd/ctl/controller.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/libirccdctl/irccd/ctl/controller.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -60,9 +60,12 @@ return; } - if (json_util::to_string(message["program"]) != "irccd") + const auto program = json_util::get_string(message, "/program"_json_pointer); + const auto major = json_util::get_int(message, "/major"_json_pointer); + + if (!program && *program != "irccd") handler(irccd_error::not_irccd, std::move(message)); - else if (json_util::to_int(message["major"]) != IRCCD_VERSION_MAJOR) + else if (major && *major != IRCCD_VERSION_MAJOR) handler(irccd_error::incompatible_version, std::move(message)); else { if (!password_.empty()) @@ -96,17 +99,19 @@ return; } - auto e = json_util::to_int(msg["error"]); - auto c = json_util::to_string(msg["errorCategory"]); + const auto e = json_util::get_int(msg, "/error"_json_pointer); + const auto c = json_util::get_string(msg, "/errorCategory"_json_pointer); - if (c == "irccd") - code = make_error_code(static_cast<irccd_error::error>(e)); - else if (c == "server") - code = make_error_code(static_cast<server_error::error>(e)); - else if (c == "plugin") - code = make_error_code(static_cast<plugin_error::error>(e)); - else if (c == "rule") - code = make_error_code(static_cast<rule_error::error>(e)); + if (e && c) { + if (*c == "irccd") + code = make_error_code(static_cast<irccd_error::error>(*e)); + else if (*c == "server") + code = make_error_code(static_cast<server_error::error>(*e)); + else if (*c == "plugin") + code = make_error_code(static_cast<plugin_error::error>(*e)); + else if (*c == "rule") + code = make_error_code(static_cast<rule_error::error>(*e)); + } handler(std::move(code), std::move(msg)); });
--- a/tests/src/libirccd/command-plugin-config/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/tests/src/libirccd/command-plugin-config/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -131,6 +131,28 @@ BOOST_AUTO_TEST_SUITE(errors) +BOOST_AUTO_TEST_CASE(invalid_identifier) +{ + boost::system::error_code result; + nlohmann::json message; + + ctl_->send({ + { "command", "plugin-config" } + }); + ctl_->recv([&] (auto rresult, auto rmessage) { + result = rresult; + message = rmessage; + }); + + wait_for([&] { + return result; + }); + + BOOST_TEST(result == plugin_error::invalid_identifier); + BOOST_TEST(message["error"].template get<int>() == plugin_error::invalid_identifier); + BOOST_TEST(message["errorCategory"].template get<std::string>() == "plugin"); +} + BOOST_AUTO_TEST_CASE(not_found) { boost::system::error_code result;
--- a/tests/src/libirccd/command-plugin-info/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/tests/src/libirccd/command-plugin-info/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -60,6 +60,28 @@ BOOST_AUTO_TEST_SUITE(errors) +BOOST_AUTO_TEST_CASE(invalid_identifier) +{ + boost::system::error_code result; + nlohmann::json message; + + ctl_->send({ + { "command", "plugin-info" } + }); + ctl_->recv([&] (auto rresult, auto rmessage) { + result = rresult; + message = rmessage; + }); + + wait_for([&] { + return result; + }); + + BOOST_TEST(result == plugin_error::invalid_identifier); + BOOST_TEST(message["error"].template get<int>() == plugin_error::invalid_identifier); + BOOST_TEST(message["errorCategory"].template get<std::string>() == "plugin"); +} + BOOST_AUTO_TEST_CASE(not_found) { boost::system::error_code result;
--- a/tests/src/libirccd/command-plugin-load/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/tests/src/libirccd/command-plugin-load/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -96,6 +96,24 @@ BOOST_AUTO_TEST_SUITE(errors) +BOOST_AUTO_TEST_CASE(invalid_identifier) +{ + boost::system::error_code result; + + ctl_->send({ + { "command", "plugin-load" } + }); + ctl_->recv([&] (auto code, auto) { + result = code; + }); + + wait_for([&] { + return result; + }); + + BOOST_TEST(result == plugin_error::invalid_identifier); +} + BOOST_AUTO_TEST_CASE(not_found) { boost::system::error_code result;
--- a/tests/src/libirccd/command-plugin-reload/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/tests/src/libirccd/command-plugin-reload/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -85,6 +85,28 @@ BOOST_AUTO_TEST_SUITE(errors) +BOOST_AUTO_TEST_CASE(invalid_identifier) +{ + boost::system::error_code result; + nlohmann::json message; + + ctl_->send({ + { "command", "plugin-reload" } + }); + ctl_->recv([&] (auto rresult, auto rmessage) { + result = rresult; + message = rmessage; + }); + + wait_for([&] { + return result; + }); + + BOOST_TEST(result == plugin_error::invalid_identifier); + BOOST_TEST(message["error"].template get<int>() == plugin_error::invalid_identifier); + BOOST_TEST(message["errorCategory"].template get<std::string>() == "plugin"); +} + BOOST_AUTO_TEST_CASE(not_found) { boost::system::error_code result;
--- a/tests/src/libirccd/command-plugin-unload/main.cpp Tue Mar 20 19:45:01 2018 +0100 +++ b/tests/src/libirccd/command-plugin-unload/main.cpp Tue Mar 20 20:01:02 2018 +0100 @@ -85,6 +85,28 @@ BOOST_AUTO_TEST_SUITE(errors) +BOOST_AUTO_TEST_CASE(invalid_identifier) +{ + boost::system::error_code result; + nlohmann::json message; + + ctl_->send({ + { "command", "plugin-unload" } + }); + ctl_->recv([&] (auto rresult, auto rmessage) { + result = rresult; + message = rmessage; + }); + + wait_for([&] { + return result; + }); + + BOOST_TEST(result == plugin_error::invalid_identifier); + BOOST_TEST(message["error"].template get<int>() == plugin_error::invalid_identifier); + BOOST_TEST(message["errorCategory"].template get<std::string>() == "plugin"); +} + BOOST_AUTO_TEST_CASE(not_found) { boost::system::error_code result;