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.
author David Demelier <markand@malikania.fr>
date Tue, 20 Mar 2018 20:01:02 +0100
parents e2ad41b02ebf
children d3ea56d7a126
files irccdctl/cli.cpp irccdctl/main.cpp irccdctl/plugin_info_cli.cpp irccdctl/watch_cli.cpp libcommon/irccd/json_util.cpp libcommon/irccd/json_util.hpp libirccd/irccd/daemon/command/plugin_config_command.cpp libirccd/irccd/daemon/command/plugin_info_command.cpp libirccd/irccd/daemon/command/plugin_load_command.cpp libirccd/irccd/daemon/command/plugin_reload_command.cpp libirccd/irccd/daemon/command/plugin_unload_command.cpp libirccd/irccd/daemon/command/rule_add_command.cpp libirccd/irccd/daemon/command/server_connect_command.cpp libirccd/irccd/daemon/command/server_disconnect_command.cpp libirccd/irccd/daemon/command/server_info_command.cpp libirccd/irccd/daemon/command/server_invite_command.cpp libirccd/irccd/daemon/command/server_join_command.cpp libirccd/irccd/daemon/command/server_kick_command.cpp libirccd/irccd/daemon/command/server_me_command.cpp libirccd/irccd/daemon/command/server_message_command.cpp libirccd/irccd/daemon/command/server_mode_command.cpp libirccd/irccd/daemon/command/server_nick_command.cpp libirccd/irccd/daemon/command/server_notice_command.cpp libirccd/irccd/daemon/command/server_part_command.cpp libirccd/irccd/daemon/command/server_reconnect_command.cpp libirccd/irccd/daemon/command/server_topic_command.cpp libirccd/irccd/daemon/plugin.cpp libirccd/irccd/daemon/plugin.hpp libirccd/irccd/daemon/server.cpp libirccd/irccd/daemon/server.hpp libirccd/irccd/daemon/server_util.cpp libirccd/irccd/daemon/server_util.hpp libirccd/irccd/daemon/service/server_service.cpp libirccd/irccd/daemon/service/server_service.hpp libirccd/irccd/daemon/service/transport_service.cpp libirccd/irccd/daemon/transport_server.cpp libirccdctl/irccd/ctl/controller.cpp tests/src/libirccd/command-plugin-config/main.cpp tests/src/libirccd/command-plugin-info/main.cpp tests/src/libirccd/command-plugin-load/main.cpp tests/src/libirccd/command-plugin-reload/main.cpp tests/src/libirccd/command-plugin-unload/main.cpp
diffstat 42 files changed, 740 insertions(+), 494 deletions(-) [+]
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;