changeset 698:ad1ee47165fa

Irccd: create own json_util::document class For convenience when parsing command arguments, use directly json_util::document instead of raw nlohmann::json object. This will simplify the process of decomposing unsafe structures.
author David Demelier <markand@malikania.fr>
date Mon, 07 May 2018 21:10:12 +0200
parents 6bc92086b2a6
children 808171f7bfa2
files irccdctl/cli.cpp irccdctl/main.cpp irccdctl/plugin_info_cli.cpp irccdctl/watch_cli.cpp libcommon/irccd/json_util.hpp libirccd/irccd/daemon/command.hpp libirccd/irccd/daemon/command/plugin_config_command.cpp libirccd/irccd/daemon/command/plugin_config_command.hpp libirccd/irccd/daemon/command/plugin_info_command.cpp libirccd/irccd/daemon/command/plugin_info_command.hpp libirccd/irccd/daemon/command/plugin_list_command.cpp libirccd/irccd/daemon/command/plugin_list_command.hpp libirccd/irccd/daemon/command/plugin_load_command.cpp libirccd/irccd/daemon/command/plugin_load_command.hpp libirccd/irccd/daemon/command/plugin_reload_command.cpp libirccd/irccd/daemon/command/plugin_reload_command.hpp libirccd/irccd/daemon/command/plugin_unload_command.cpp libirccd/irccd/daemon/command/plugin_unload_command.hpp libirccd/irccd/daemon/command/rule_add_command.cpp libirccd/irccd/daemon/command/rule_add_command.hpp libirccd/irccd/daemon/command/rule_edit_command.cpp libirccd/irccd/daemon/command/rule_edit_command.hpp libirccd/irccd/daemon/command/rule_info_command.cpp libirccd/irccd/daemon/command/rule_info_command.hpp libirccd/irccd/daemon/command/rule_list_command.cpp libirccd/irccd/daemon/command/rule_list_command.hpp libirccd/irccd/daemon/command/rule_move_command.cpp libirccd/irccd/daemon/command/rule_move_command.hpp libirccd/irccd/daemon/command/rule_remove_command.cpp libirccd/irccd/daemon/command/rule_remove_command.hpp libirccd/irccd/daemon/command/server_connect_command.cpp libirccd/irccd/daemon/command/server_connect_command.hpp libirccd/irccd/daemon/command/server_disconnect_command.cpp libirccd/irccd/daemon/command/server_disconnect_command.hpp libirccd/irccd/daemon/command/server_info_command.cpp libirccd/irccd/daemon/command/server_info_command.hpp libirccd/irccd/daemon/command/server_invite_command.cpp libirccd/irccd/daemon/command/server_invite_command.hpp libirccd/irccd/daemon/command/server_join_command.cpp libirccd/irccd/daemon/command/server_join_command.hpp libirccd/irccd/daemon/command/server_kick_command.cpp libirccd/irccd/daemon/command/server_kick_command.hpp libirccd/irccd/daemon/command/server_list_command.cpp libirccd/irccd/daemon/command/server_list_command.hpp libirccd/irccd/daemon/command/server_me_command.cpp libirccd/irccd/daemon/command/server_me_command.hpp libirccd/irccd/daemon/command/server_message_command.cpp libirccd/irccd/daemon/command/server_message_command.hpp libirccd/irccd/daemon/command/server_mode_command.cpp libirccd/irccd/daemon/command/server_mode_command.hpp libirccd/irccd/daemon/command/server_nick_command.cpp libirccd/irccd/daemon/command/server_nick_command.hpp libirccd/irccd/daemon/command/server_notice_command.cpp libirccd/irccd/daemon/command/server_notice_command.hpp libirccd/irccd/daemon/command/server_part_command.cpp libirccd/irccd/daemon/command/server_part_command.hpp libirccd/irccd/daemon/command/server_reconnect_command.cpp libirccd/irccd/daemon/command/server_reconnect_command.hpp libirccd/irccd/daemon/command/server_topic_command.cpp libirccd/irccd/daemon/command/server_topic_command.hpp libirccd/irccd/daemon/server_util.cpp libirccd/irccd/daemon/service/transport_service.cpp libirccd/irccd/daemon/transport_server.cpp libirccdctl/irccd/ctl/controller.cpp
diffstat 64 files changed, 145 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- a/irccdctl/cli.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/irccdctl/cli.cpp	Mon May 07 21:10:12 2018 +0200
@@ -34,7 +34,7 @@
         if (code)
             throw std::system_error(code);
 
-        const auto c = json_util::parser(message).get<std::string>("command");
+        const auto c = json_util::document(message).get<std::string>("command");
 
         if (!c) {
             recv_response(ctl, std::move(req), std::move(handler));
--- a/irccdctl/main.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/irccdctl/main.cpp	Mon May 07 21:10:12 2018 +0200
@@ -531,10 +531,10 @@
             throw std::system_error(code);
 
         if (verbose) {
-            const json_util::parser parser(info);
-            const auto major = parser.get<int>("/major");
-            const auto minor = parser.get<int>("/minor");
-            const auto patch = parser.get<int>("/patch");
+            const json_util::document doc(info);
+            const auto major = doc.get<int>("/major");
+            const auto minor = doc.get<int>("/minor");
+            const auto patch = doc.get<int>("/patch");
 
             if (!major || !minor || !patch)
                 std::cout << "connected to irccd (unknown version)" << std::endl;
--- a/irccdctl/plugin_info_cli.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/irccdctl/plugin_info_cli.cpp	Mon May 07 21:10:12 2018 +0200
@@ -42,17 +42,17 @@
     });
 
     request(ctl, json, [] (auto result) {
-        const json_util::parser parser(result);
+        const json_util::document doc(result);
 
         std::cout << std::boolalpha;
         std::cout << "Author         : " <<
-            parser.get<std::string>("author").value_or("(unknown)") << std::endl;
+            doc.get<std::string>("author").value_or("(unknown)") << std::endl;
         std::cout << "License        : " <<
-            parser.get<std::string>("license").value_or("(unknown)") << std::endl;
+            doc.get<std::string>("license").value_or("(unknown)") << std::endl;
         std::cout << "Summary        : " <<
-            parser.get<std::string>("summary").value_or("(unknown)") << std::endl;
+            doc.get<std::string>("summary").value_or("(unknown)") << std::endl;
         std::cout << "Version        : " <<
-            parser.get<std::string>("version").value_or("(unknown)") << std::endl;
+            doc.get<std::string>("version").value_or("(unknown)") << std::endl;
     });
 }
 
--- a/irccdctl/watch_cli.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/irccdctl/watch_cli.cpp	Mon May 07 21:10:12 2018 +0200
@@ -182,7 +182,7 @@
         if (code)
             throw std::system_error(code);
 
-        const auto event = json_util::parser(message).get<std::string>("event");
+        const auto event = json_util::document(message).get<std::string>("event");
         const auto it = events.find(event ? *event : "");
 
         if (it != events.end()) {
--- a/libcommon/irccd/json_util.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libcommon/irccd/json_util.hpp	Mon May 07 21:10:12 2018 +0200
@@ -260,18 +260,15 @@
  * This class helps destructuring insecure JSON input by returning optional
  * values if they are not present or invalid.
  */
-class parser {
-private:
-    nlohmann::json json_;
-
+class document : public nlohmann::json {
 public:
     /**
-     * Construct the parser.
+     * Constructor.
      *
-     * \param document the document
+     * \param object the object
      */
-    inline parser(nlohmann::json document) noexcept
-        : json_(std::move(document))
+    inline document(nlohmann::json object)
+        : nlohmann::json(std::move(object))
     {
     }
 
@@ -279,16 +276,16 @@
      * Get a value from the document object.
      *
      * \param key the property key
-     * \return the value or boost::none if not found
+     * \return the value or boost::none if not found or not convertible
      */
     template <typename Type>
     inline boost::optional<Type> get(const std::string& key) const noexcept
     {
         static_assert(parser_type_traits<Type>::value, "type not supported");
 
-        const auto it = json_.find(key);
+        const auto it = find(key);
 
-        if (it == json_.end())
+        if (it == end())
             return boost::none;
 
         return parser_type_traits<Type>::get(*it);
@@ -309,9 +306,9 @@
     {
         static_assert(parser_type_traits<Type>::value, "type not supported");
 
-        const auto it = json_.find(key);
+        const auto it = find(key);
 
-        if (it == json_.end())
+        if (it == end())
             return boost::optional<Type>(std::forward<DefaultValue>(def));
 
         return parser_type_traits<Type>::get(*it);
@@ -362,6 +359,7 @@
     return false;
 }
 
+
 } // !json_util
 
 } // !irccd
--- a/libirccd/irccd/daemon/command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -24,11 +24,10 @@
  * \brief Remote commands.
  */
 
-#include <irccd/sysconfig.hpp>
-
 #include <string>
 
-#include "json.hpp"
+#include <irccd/sysconfig.hpp>
+#include <irccd/json_util.hpp>
 
 namespace irccd {
 
@@ -42,6 +41,11 @@
 class command {
 public:
     /**
+     * Convenient alias.
+     */
+    using document = json_util::document;
+
+    /**
      * Default destructor virtual.
      */
     virtual ~command() = default;
@@ -66,7 +70,7 @@
      * \param client the client
      * \param args the client arguments
      */
-    virtual void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) = 0;
+    virtual void exec(irccd& irccd, transport_client& client, const document& args) = 0;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_config_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_config_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -79,9 +79,9 @@
     return "plugin-config";
 }
 
-void plugin_config_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void plugin_config_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("plugin");
+    const auto id = args.get<std::string>("plugin");
 
     if (!id || !string_util::is_identifier(*id))
         throw plugin_error(plugin_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/plugin_config_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_config_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_info_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_info_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "plugin-info";
 }
 
-void plugin_info_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void plugin_info_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("plugin");
+    const auto id = args.get<std::string>("plugin");
 
     if (!id || !string_util::is_identifier(*id))
         throw plugin_error(plugin_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/plugin_info_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_info_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_list_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_list_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -30,7 +30,7 @@
     return "plugin-list";
 }
 
-void plugin_list_command::exec(irccd& irccd, transport_client& client, const nlohmann::json&)
+void plugin_list_command::exec(irccd& irccd, transport_client& client, const document&)
 {
     auto list = nlohmann::json::array();
 
--- a/libirccd/irccd/daemon/command/plugin_list_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_list_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -41,7 +41,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_load_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_load_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "plugin-load";
 }
 
-void plugin_load_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void plugin_load_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("plugin");
+    const auto id = args.get<std::string>("plugin");
 
     if (!id || !string_util::is_identifier(*id))
         throw plugin_error(plugin_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/plugin_load_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_load_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_reload_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_reload_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "plugin-reload";
 }
 
-void plugin_reload_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void plugin_reload_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("plugin");
+    const auto id = args.get<std::string>("plugin");
 
     if (!id || !string_util::is_identifier(*id))
         throw plugin_error(plugin_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/plugin_reload_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_reload_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_unload_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_unload_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "plugin-unload";
 }
 
-void plugin_unload_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void plugin_unload_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("plugin");
+    const auto id = args.get<std::string>("plugin");
 
     if (!id || !string_util::is_identifier(*id))
         throw plugin_error(plugin_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/plugin_unload_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_unload_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_add_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_add_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "rule-add";
 }
 
-void rule_add_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void rule_add_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto index = json_util::parser(args).optional<unsigned>("index", irccd.rules().length());
+    const auto index = args.optional<unsigned>("index", irccd.rules().length());
 
     if (!index || *index > irccd.rules().length())
         throw rule_error(rule_error::error::invalid_index);
--- a/libirccd/irccd/daemon/command/rule_add_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_add_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_edit_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_edit_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -35,7 +35,7 @@
     return "rule-edit";
 }
 
-void rule_edit_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void rule_edit_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
     static const auto updateset = [] (auto& set, auto args, const auto& key) {
         for (const auto& v : args["remove-"s + key]) {
@@ -48,7 +48,7 @@
         }
     };
 
-    const auto index = json_util::parser(args).get<unsigned>("index");
+    const auto index = args.get<unsigned>("index");
 
     if (!index)
         throw rule_error(rule_error::invalid_index);
--- a/libirccd/irccd/daemon/command/rule_edit_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_edit_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_info_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_info_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,9 +33,9 @@
     return "rule-info";
 }
 
-void rule_info_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void rule_info_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto index = json_util::parser(args).get<unsigned>("index");
+    const auto index = args.get<unsigned>("index");
 
     if (!index)
         throw rule_error(rule_error::invalid_index);
--- a/libirccd/irccd/daemon/command/rule_info_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_info_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_list_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_list_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -31,7 +31,7 @@
     return "rule-list";
 }
 
-void rule_list_command::exec(irccd& irccd, transport_client& client, const nlohmann::json&)
+void rule_list_command::exec(irccd& irccd, transport_client& client, const document&)
 {
     auto array = nlohmann::json::array();
 
--- a/libirccd/irccd/daemon/command/rule_list_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_list_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -41,7 +41,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_move_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_move_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,11 +33,10 @@
     return "rule-move";
 }
 
-void rule_move_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void rule_move_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto from = parser.get<unsigned>("from");
-    const auto to = parser.get<unsigned>("to");
+    const auto from = args.get<unsigned>("from");
+    const auto to = args.get<unsigned>("to");
 
     if (!from || !to)
         throw rule_error(rule_error::invalid_index);
--- a/libirccd/irccd/daemon/command/rule_move_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_move_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/rule_remove_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_remove_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -32,9 +32,9 @@
     return "rule-remove";
 }
 
-void rule_remove_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void rule_remove_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto index = json_util::parser(args).get<unsigned>("index");
+    const auto index = args.get<unsigned>("index");
 
     if (!index || *index >= irccd.rules().length())
         throw rule_error(rule_error::invalid_index);
--- a/libirccd/irccd/daemon/command/rule_remove_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/rule_remove_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -45,7 +45,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_connect_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_connect_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -31,7 +31,7 @@
     return "server-connect";
 }
 
-void server_connect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_connect_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
     auto server = server_util::from_json(irccd.get_service(), args);
 
--- a/libirccd/irccd/daemon/command/server_connect_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_connect_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -49,7 +49,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_disconnect_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_disconnect_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,7 +33,7 @@
     return "server-disconnect";
 }
 
-void server_disconnect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_disconnect_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
     const auto it = args.find("server");
 
--- a/libirccd/irccd/daemon/command/server_disconnect_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_disconnect_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_info_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_info_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -34,9 +34,9 @@
     return "server-info";
 }
 
-void server_info_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_info_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const auto id = json_util::parser(args).get<std::string>("server");
+    const auto id = args.get<std::string>("server");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
@@ -44,7 +44,7 @@
     const auto server = irccd.servers().require(*id);
 
     // Construct the JSON response.
-    auto response = nlohmann::json::object();
+    auto response = document::object();
 
     // General stuff.
     response.push_back({"command", "server-info"});
--- a/libirccd/irccd/daemon/command/server_info_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_info_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_invite_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_invite_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-invite";
 }
 
-void server_invite_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_invite_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto target = parser.get<std::string>("target");
-    const auto channel = parser.get<std::string>("channel");
+    const auto id = args.get<std::string>("server");
+    const auto target = args.get<std::string>("target");
+    const auto channel = args.get<std::string>("channel");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_invite_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_invite_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -48,7 +48,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_join_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_join_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-join";
 }
 
-void server_join_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_join_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("channel");
-    const auto password = parser.optional<std::string>("password", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("channel");
+    const auto password = args.optional<std::string>("password", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_join_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_join_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_kick_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_kick_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,13 +33,12 @@
     return "server-kick";
 }
 
-void server_kick_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_kick_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto target = parser.get<std::string>("target");
-    const auto channel = parser.get<std::string>("channel");
-    const auto reason = parser.optional<std::string>("reason", "");
+    const auto id = args.get<std::string>("server");
+    const auto target = args.get<std::string>("target");
+    const auto channel = args.get<std::string>("channel");
+    const auto reason = args.optional<std::string>("reason", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_kick_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_kick_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -48,7 +48,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_list_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_list_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -30,7 +30,7 @@
     return "server-list";
 }
 
-void server_list_command::exec(irccd& irccd, transport_client& client, const nlohmann::json&)
+void server_list_command::exec(irccd& irccd, transport_client& client, const document&)
 {
     auto json = nlohmann::json::object();
     auto list = nlohmann::json::array();
--- a/libirccd/irccd/daemon/command/server_list_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_list_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -41,7 +41,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_me_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_me_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-me";
 }
 
-void server_me_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_me_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("target");
-    const auto message = parser.optional<std::string>("message", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("target");
+    const auto message = args.optional<std::string>("message", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_me_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_me_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_message_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_message_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-message";
 }
 
-void server_message_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_message_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("target");
-    const auto message = parser.optional<std::string>("message", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("target");
+    const auto message = args.optional<std::string>("message", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_message_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_message_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_mode_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_mode_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,15 +33,14 @@
     return "server-mode";
 }
 
-void server_mode_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_mode_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("channel");
-    const auto mode = parser.get<std::string>("mode");
-    const auto limit = parser.optional<std::string>("limit", "");
-    const auto user = parser.optional<std::string>("user", "");
-    const auto mask = parser.optional<std::string>("mask", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("channel");
+    const auto mode = args.get<std::string>("mode");
+    const auto limit = args.optional<std::string>("limit", "");
+    const auto user = args.optional<std::string>("user", "");
+    const auto mask = args.optional<std::string>("mask", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_mode_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_mode_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -48,7 +48,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_nick_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_nick_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,11 +33,10 @@
     return "server-nick";
 }
 
-void server_nick_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_nick_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto nick = parser.get<std::string>("nickname");
+    const auto id = args.get<std::string>("server");
+    const auto nick = args.get<std::string>("nickname");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_nick_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_nick_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_notice_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_notice_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-notice";
 }
 
-void server_notice_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_notice_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("target");
-    const auto message = parser.optional<std::string>("message", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("target");
+    const auto message = args.optional<std::string>("message", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_notice_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_notice_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_part_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_part_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-part";
 }
 
-void server_part_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_part_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("channel");
-    const auto reason = parser.optional<std::string>("reason", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("channel");
+    const auto reason = args.optional<std::string>("reason", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_part_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_part_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_reconnect_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_reconnect_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,7 +33,7 @@
     return "server-reconnect";
 }
 
-void server_reconnect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_reconnect_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
     const auto it = args.find("server");
 
--- a/libirccd/irccd/daemon/command/server_reconnect_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_reconnect_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -46,7 +46,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/server_topic_command.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_topic_command.cpp	Mon May 07 21:10:12 2018 +0200
@@ -33,12 +33,11 @@
     return "server-topic";
 }
 
-void server_topic_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args)
+void server_topic_command::exec(irccd& irccd, transport_client& client, const document& args)
 {
-    const json_util::parser parser(args);
-    const auto id = parser.get<std::string>("server");
-    const auto channel = parser.get<std::string>("channel");
-    const auto topic = parser.optional<std::string>("topic", "");
+    const auto id = args.get<std::string>("server");
+    const auto channel = args.get<std::string>("channel");
+    const auto topic = args.optional<std::string>("topic", "");
 
     if (!id || !string_util::is_identifier(*id))
         throw server_error(server_error::invalid_identifier);
--- a/libirccd/irccd/daemon/command/server_topic_command.hpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/command/server_topic_command.hpp	Mon May 07 21:10:12 2018 +0200
@@ -47,7 +47,7 @@
     /**
      * \copydoc command::exec
      */
-    void exec(irccd& irccd, transport_client& client, const nlohmann::json& args) override;
+    void exec(irccd& irccd, transport_client& client, const document& args) override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/server_util.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/server_util.cpp	Mon May 07 21:10:12 2018 +0200
@@ -119,7 +119,7 @@
     sv.set_command_char(command_char);
 }
 
-void from_json_load_options(server& sv, const json_util::parser& parser)
+void from_json_load_options(server& sv, const json_util::document& parser)
 {
     const auto port = parser.optional<std::uint16_t>("port", sv.get_port());
     const auto nickname = parser.optional<std::string>("nickname", sv.get_nickname());
@@ -153,7 +153,7 @@
     sv.set_password(*password);
 }
 
-void from_json_load_flags(server& sv, const json_util::parser& parser)
+void from_json_load_flags(server& sv, const json_util::document& parser)
 {
     const auto ipv6 = parser.get<bool>("ipv6");
     const auto auto_rejoin = parser.get<bool>("autoRejoin");
@@ -183,7 +183,7 @@
 std::shared_ptr<server> from_json(boost::asio::io_service& service, const nlohmann::json& object)
 {
     // Mandatory parameters.
-    const json_util::parser parser(object);
+    const json_util::document parser(object);
     const auto id = parser.get<std::string>("name");
     const auto host = parser.get<std::string>("host");
 
--- a/libirccd/irccd/daemon/service/transport_service.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/service/transport_service.cpp	Mon May 07 21:10:12 2018 +0200
@@ -32,15 +32,12 @@
 
 namespace irccd {
 
-namespace {
-
-} // !namespace
-
 void transport_service::handle_command(std::shared_ptr<transport_client> tc, const nlohmann::json& object)
 {
     assert(object.is_object());
 
-    const auto name = json_util::parser(object).get<std::string>("command");
+    const json_util::document doc(object);
+    const auto name = doc.get<std::string>("command");
 
     if (!name) {
         tc->error(irccd_error::invalid_message);
@@ -55,7 +52,7 @@
         tc->error(irccd_error::invalid_command, *name);
     else {
         try {
-            (*cmd)->exec(irccd_, *tc, object);
+            (*cmd)->exec(irccd_, *tc, doc);
         } catch (const std::system_error& ex) {
             tc->error(ex.code(), (*cmd)->get_name());
         } catch (const std::exception& ex) {
--- a/libirccd/irccd/daemon/transport_server.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccd/irccd/daemon/transport_server.cpp	Mon May 07 21:10:12 2018 +0200
@@ -39,9 +39,9 @@
             return;
         }
 
-        const json_util::parser parser(message);
-        const auto command = parser.get<std::string>("command");
-        const auto password = parser.get<std::string>("password");
+        const json_util::document doc(message);
+        const auto command = doc.get<std::string>("command");
+        const auto password = doc.get<std::string>("password");
 
         if (!command || *command != "auth") {
             client->error(irccd_error::auth_required);
--- a/libirccdctl/irccd/ctl/controller.cpp	Fri May 04 14:19:01 2018 +0200
+++ b/libirccdctl/irccd/ctl/controller.cpp	Mon May 07 21:10:12 2018 +0200
@@ -59,9 +59,9 @@
             return;
         }
 
-        const json_util::parser parser(message);
-        const auto program = parser.get<std::string>("program");
-        const auto major = parser.get<int>("major");
+        const json_util::document doc(message);
+        const auto program = doc.get<std::string>("program");
+        const auto major = doc.get<int>("major");
 
         if (!program && *program != "irccd")
             handler(irccd_error::not_irccd, std::move(message));
@@ -104,9 +104,9 @@
             return;
         }
 
-        const json_util::parser parser(msg);
-        const auto e = parser.get<int>("error");
-        const auto c = parser.get<std::string>("errorCategory");
+        const json_util::document doc(msg);
+        const auto e = doc.get<int>("error");
+        const auto c = doc.get<std::string>("errorCategory");
 
         if (e && c) {
             if (*c == "irccd")