Mercurial > irccd
changeset 826:f85faf0f5d70
irccd: rename command to transport_command
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 08 Jan 2019 21:47:59 +0100 |
parents | e9da936309df |
children | 2ecff01d4277 |
files | irccd/main.cpp libirccd-daemon/CMakeLists.txt libirccd-daemon/irccd/daemon/command.cpp libirccd-daemon/irccd/daemon/command.hpp libirccd-daemon/irccd/daemon/transport_command.cpp libirccd-daemon/irccd/daemon/transport_command.hpp libirccd-daemon/irccd/daemon/transport_service.cpp libirccd-daemon/irccd/daemon/transport_service.hpp libirccd-test/irccd/test/cli_fixture.cpp libirccd-test/irccd/test/command_fixture.cpp |
diffstat | 10 files changed, 1681 insertions(+), 1690 deletions(-) [+] |
line wrap: on
line diff
--- a/irccd/main.cpp Tue Jan 08 20:41:20 2019 +0100 +++ b/irccd/main.cpp Tue Jan 08 21:47:59 2019 +0100 @@ -27,11 +27,11 @@ #include <irccd/options.hpp> #include <irccd/system.hpp> -#include <irccd/daemon/command.hpp> #include <irccd/daemon/dynlib_plugin.hpp> #include <irccd/daemon/bot.hpp> #include <irccd/daemon/logger.hpp> #include <irccd/daemon/plugin_service.hpp> +#include <irccd/daemon/transport_command.hpp> #include <irccd/daemon/transport_service.hpp> #if defined(IRCCD_HAVE_JS) @@ -178,7 +178,7 @@ init(argc, argv); // 1. Load commands. - for (const auto& f : command::registry()) + for (const auto& f : transport_command::registry()) instance->transports().get_commands().push_back(f()); // 2. Load plugin loaders.
--- a/libirccd-daemon/CMakeLists.txt Tue Jan 08 20:41:20 2019 +0100 +++ b/libirccd-daemon/CMakeLists.txt Tue Jan 08 21:47:59 2019 +0100 @@ -23,8 +23,6 @@ ${libirccd-daemon_SOURCE_DIR}/irccd/daemon.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/bot.cpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/bot.hpp - ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/command.cpp - ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/command.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/dynlib_plugin.cpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/dynlib_plugin.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/irc.cpp @@ -49,6 +47,8 @@ ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/server_util.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_client.cpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_client.hpp + ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_command.cpp + ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_command.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_server.cpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_server.hpp ${libirccd-daemon_SOURCE_DIR}/irccd/daemon/transport_service.cpp
--- a/libirccd-daemon/irccd/daemon/command.cpp Tue Jan 08 20:41:20 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,863 +0,0 @@ -/* - * command.cpp -- remote command - * - * Copyright (c) 2013-2019 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 - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/sysconfig.hpp> - -#include <irccd/string_util.hpp> - -#include "bot.hpp" -#include "command.hpp" -#include "plugin.hpp" -#include "plugin_service.hpp" -#include "rule.hpp" -#include "rule_service.hpp" -#include "rule_util.hpp" -#include "server.hpp" -#include "server_service.hpp" -#include "server_util.hpp" -#include "transport_client.hpp" - -using namespace std::string_literals; - -namespace irccd::daemon { - -namespace { - -void exec_set(transport_client& client, plugin& plugin, const nlohmann::json& args) -{ - assert(args.count("value") > 0); - - const auto var = args.find("variable"); - const auto value = args.find("value"); - - if (var == args.end() || !var->is_string()) - throw bot_error(bot_error::error::incomplete_message); - if (value == args.end() || !value->is_string()) - throw bot_error(bot_error::error::incomplete_message); - - auto config = plugin.get_options(); - - config[var->get<std::string>()] = value->get<std::string>(); - plugin.set_options(config); - client.success("plugin-config"); -} - -void exec_get(transport_client& client, plugin& plugin, const nlohmann::json& args) -{ - auto variables = nlohmann::json::object(); - auto var = args.find("variable"); - - if (var != args.end() && var->is_string()) - variables[var->get<std::string>()] = plugin.get_options()[*var]; - else - for (const auto& pair : plugin.get_options()) - variables[pair.first] = pair.second; - - /* - * Don't put all variables into the response, put them into a sub - * property 'variables' instead. - * - * It's easier for the client to iterate over all. - */ - client.write({ - { "command", "plugin-config" }, - { "variables", variables } - }); -} - -template <typename T> -auto bind() noexcept -> command::constructor -{ - return [] () noexcept { - return std::make_unique<T>(); - }; -} - -} // !namespace - -auto command::registry() noexcept -> const std::vector<constructor>& -{ - static const std::vector<command::constructor> list{ - bind<plugin_config_command>(), - bind<plugin_info_command>(), - bind<plugin_list_command>(), - bind<plugin_load_command>(), - bind<plugin_reload_command>(), - bind<plugin_unload_command>(), - bind<rule_add_command>(), - bind<rule_edit_command>(), - bind<rule_info_command>(), - bind<rule_info_command>(), - bind<rule_list_command>(), - bind<rule_move_command>(), - bind<rule_remove_command>(), - bind<server_connect_command>(), - bind<server_disconnect_command>(), - bind<server_info_command>(), - bind<server_invite_command>(), - bind<server_join_command>(), - bind<server_kick_command>(), - bind<server_list_command>(), - bind<server_me_command>(), - bind<server_message_command>(), - bind<server_mode_command>(), - bind<server_nick_command>(), - bind<server_notice_command>(), - bind<server_part_command>(), - bind<server_reconnect_command>(), - bind<server_topic_command>() - }; - - return list; -}; - -// {{{ plugin_config_command - -auto plugin_config_command::get_name() const noexcept -> std::string_view -{ - return "plugin-config"; -} - -void plugin_config_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("plugin"); - - if (!id || !string_util::is_identifier(*id)) - throw plugin_error(plugin_error::invalid_identifier); - - const auto plugin = bot.plugins().require(*id); - - if (args.count("value") > 0) - exec_set(client, *plugin, args); - else - exec_get(client, *plugin, args); -} - -// }}} - -// {{{ plugin_info_command - -auto plugin_info_command::get_name() const noexcept -> std::string_view -{ - return "plugin-info"; -} - -void plugin_info_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("plugin"); - - if (!id || !string_util::is_identifier(*id)) - throw plugin_error(plugin_error::invalid_identifier); - - const auto plugin = bot.plugins().require(*id); - - client.write({ - { "command", "plugin-info" }, - { "author", std::string(plugin->get_author()) }, - { "license", std::string(plugin->get_license()) }, - { "summary", std::string(plugin->get_summary()) }, - { "version", std::string(plugin->get_version()) } - }); -} - -// }}} - -// {{{ plugin_list_command - -auto plugin_list_command::get_name() const noexcept -> std::string_view -{ - return "plugin-list"; -} - -void plugin_list_command::exec(bot& bot, transport_client& client, const document&) -{ - auto list = nlohmann::json::array(); - - for (const auto& plg : bot.plugins().list()) - list += plg->get_id(); - - client.write({ - { "command", "plugin-list" }, - { "list", list } - }); -} - -// }}} - -// {{{ plugin_load_command - -auto plugin_load_command::get_name() const noexcept -> std::string_view -{ - return "plugin-load"; -} - -void plugin_load_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("plugin"); - - if (!id || !string_util::is_identifier(*id)) - throw plugin_error(plugin_error::invalid_identifier); - - bot.plugins().load(*id, ""); - client.success("plugin-load"); -} - -// }}} - -// {{{ plugin_reload_command - -auto plugin_reload_command::get_name() const noexcept -> std::string_view -{ - return "plugin-reload"; -} - -void plugin_reload_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("plugin"); - - if (!id || !string_util::is_identifier(*id)) - throw plugin_error(plugin_error::invalid_identifier); - - bot.plugins().reload(*id); - client.success("plugin-reload"); -} - -// }}} - -// {{{ plugin_unload_command - -auto plugin_unload_command::get_name() const noexcept -> std::string_view -{ - return "plugin-unload"; -} - -void plugin_unload_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("plugin"); - - if (!id || !string_util::is_identifier(*id)) - throw plugin_error(plugin_error::invalid_identifier); - - bot.plugins().unload(*id); - client.success("plugin-unload"); -} - -// }}} - -// {{{ rule_add_command - -auto rule_add_command::get_name() const noexcept -> std::string_view -{ - return "rule-add"; -} - -void rule_add_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto index = args.optional<unsigned>("index", bot.rules().list().size()); - - if (!index || *index > bot.rules().list().size()) - throw rule_error(rule_error::error::invalid_index); - - bot.rules().insert(rule_util::from_json(args), *index); - client.success("rule-add"); -} - -// }}} - -// {{{ rule_edit_command - -auto rule_edit_command::get_name() const noexcept -> std::string_view -{ - return "rule-edit"; -} - -void rule_edit_command::exec(bot& bot, 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]) { - if (v.is_string()) - set.erase(v.template get<std::string>()); - } - for (const auto& v : args["add-"s + key]) { - if (v.is_string()) - set.insert(v.template get<std::string>()); - } - }; - - const auto index = args.get<unsigned>("index"); - - if (!index) - throw rule_error(rule_error::invalid_index); - - // Create a copy to avoid incomplete edition in case of errors. - auto rule = bot.rules().require(*index); - - updateset(rule.channels, args, "channels"); - updateset(rule.events, args, "events"); - updateset(rule.plugins, args, "plugins"); - updateset(rule.servers, args, "servers"); - - auto action = args.find("action"); - - if (action != args.end()) { - if (!action->is_string()) - throw rule_error(rule_error::error::invalid_action); - - if (action->get<std::string>() == "accept") - rule.action = rule::action_type::accept; - else if (action->get<std::string>() == "drop") - rule.action = rule::action_type::drop; - else - throw rule_error(rule_error::invalid_action); - } - - // All done, sync the rule. - bot.rules().require(*index) = rule; - client.success("rule-edit"); -} - -// }}} - -// {{{ rule_info_command - -auto rule_info_command::get_name() const noexcept -> std::string_view -{ - return "rule-info"; -} - -void rule_info_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto index = args.get<unsigned>("index"); - - if (!index) - throw rule_error(rule_error::invalid_index); - - auto json = rule_util::to_json(bot.rules().require(*index)); - - json.push_back({"command", "rule-info"}); - client.write(std::move(json)); -} - -// }}} - -// {{{ rule_list_command - -auto rule_list_command::get_name() const noexcept -> std::string_view -{ - return "rule-list"; -} - -void rule_list_command::exec(bot& bot, transport_client& client, const document&) -{ - auto array = nlohmann::json::array(); - - for (const auto& rule : bot.rules().list()) - array.push_back(rule_util::to_json(rule)); - - client.write({ - { "command", "rule-list" }, - { "list", std::move(array) } - }); -} - -// }}} - -// {{{ rule_move_command - -auto rule_move_command::get_name() const noexcept -> std::string_view -{ - return "rule-move"; -} - -void rule_move_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto from = args.get<unsigned>("from"); - const auto to = args.get<unsigned>("to"); - - if (!from || !to) - throw rule_error(rule_error::invalid_index); - - /* - * Examples of moves - * -------------------------------------------------------------- - * - * Before: [0] [1] [2] - * - * from = 0 - * to = 2 - * - * After: [1] [2] [0] - * - * -------------------------------------------------------------- - * - * Before: [0] [1] [2] - * - * from = 2 - * to = 0 - * - * After: [2] [0] [1] - * - * -------------------------------------------------------------- - * - * Before: [0] [1] [2] - * - * from = 0 - * to = 123 - * - * After: [1] [2] [0] - */ - - // Ignore dumb input. - if (*from == *to) { - client.success("rule-move"); - return; - } - - if (*from >= bot.rules().list().size()) - throw rule_error(rule_error::error::invalid_index); - - const auto save = bot.rules().list()[*from]; - - bot.rules().remove(*from); - bot.rules().insert(save, *to > bot.rules().list().size() ? bot.rules().list().size() : *to); - client.success("rule-move"); -} - -// }}} - -// {{{ rule_remove_command - -auto rule_remove_command::get_name() const noexcept -> std::string_view -{ - return "rule-remove"; -} - -void rule_remove_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto index = args.get<unsigned>("index"); - - if (!index || *index >= bot.rules().list().size()) - throw rule_error(rule_error::invalid_index); - - bot.rules().remove(*index); - client.success("rule-remove"); -} - -// }}} - -// {{{ server_connect_command - -auto server_connect_command::get_name() const noexcept -> std::string_view -{ - return "server-connect"; -} - -void server_connect_command::exec(bot& bot, transport_client& client, const document& args) -{ - auto server = server_util::from_json(bot.get_service(), args); - - if (bot.servers().has(server->get_id())) - throw server_error(server_error::already_exists); - - bot.servers().add(std::move(server)); - client.success("server-connect"); -} - -// }}} - -// {{{ server_disconnect_command - -auto server_disconnect_command::get_name() const noexcept -> std::string_view -{ - return "server-disconnect"; -} - -void server_disconnect_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto it = args.find("server"); - - if (it == args.end()) - bot.servers().clear(); - else { - if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) - throw server_error(server_error::invalid_identifier); - - const auto name = it->get<std::string>(); - - bot.servers().require(name); - bot.servers().remove(name); - } - - client.success("server-disconnect"); -} - -// }}} - -// {{{ server_info_command - -auto server_info_command::get_name() const noexcept -> std::string_view -{ - return "server-info"; -} - -void server_info_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto id = args.get<std::string>("server"); - - if (!id || !string_util::is_identifier(*id)) - throw server_error(server_error::invalid_identifier); - - const auto server = bot.servers().require(*id); - - // Construct the JSON response. - auto response = document::object(); - - // General stuff. - response.push_back({"command", "server-info"}); - response.push_back({"name", server->get_id()}); - response.push_back({"hostname", server->get_hostname()}); - response.push_back({"port", server->get_port()}); - response.push_back({"nickname", server->get_nickname()}); - response.push_back({"username", server->get_username()}); - response.push_back({"realname", server->get_realname()}); - response.push_back({"channels", server->get_channels()}); - - // Optional stuff. - response.push_back({"ipv4", static_cast<bool>(server->get_options() & server::options::ipv4)}); - response.push_back({"ipv6", static_cast<bool>(server->get_options() & server::options::ipv6)}); - response.push_back({"ssl", static_cast<bool>(server->get_options() & server::options::ssl)}); - - client.write(response); -} - -// }}} - -// {{{ server_invite_command - -auto server_invite_command::get_name() const noexcept -> std::string_view -{ - return "server-invite"; -} - -void server_invite_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!target || target->empty()) - throw server_error(server_error::invalid_nickname); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - - bot.servers().require(*id)->invite(*target, *channel); - client.success("server-invite"); -} - -// }}} - -// {{{ server_join_command - -auto server_join_command::get_name() const noexcept -> std::string_view -{ - return "server-join"; -} - -void server_join_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!password) - throw server_error(server_error::invalid_password); - - bot.servers().require(*id)->join(*channel, *password); - client.success("server-join"); -} - -// }}} - -// {{{ server_kick_command - -auto server_kick_command::get_name() const noexcept -> std::string_view -{ - return "server-kick"; -} - -void server_kick_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!target || target->empty()) - throw server_error(server_error::invalid_nickname); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!reason) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->kick(*target, *channel, *reason); - client.success("server-kick"); -} - -// }}} - -// {{{ server_list_command - -auto server_list_command::get_name() const noexcept -> std::string_view -{ - return "server-list"; -} - -void server_list_command::exec(bot& bot, transport_client& client, const document&) -{ - auto json = nlohmann::json::object(); - auto list = nlohmann::json::array(); - - for (const auto& server : bot.servers().list()) - list.push_back(server->get_id()); - - client.write({ - { "command", "server-list" }, - { "list", std::move(list) } - }); -} - -// }}} - -// {{{ server_me_command - -auto server_me_command::get_name() const noexcept -> std::string_view -{ - return "server-me"; -} - -void server_me_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!message) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->me(*channel, *message); - client.success("server-me"); -} - -// }}} - -// {{{ server_message_command - -auto server_message_command::get_name() const noexcept -> std::string_view -{ - return "server-message"; -} - -void server_message_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!message) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->message(*channel, *message); - client.success("server-message"); -} - -// }}} - -// {{{ server_mode_command - -auto server_mode_command::get_name() const noexcept -> std::string_view -{ - return "server-mode"; -} - -void server_mode_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!mode || mode->empty()) - throw server_error(server_error::invalid_mode); - if (!limit || !user || !mask) - throw server_error(server_error::invalid_mode); - - bot.servers().require(*id)->mode(*channel, *mode, *limit, *user, *mask); - client.success("server-mode"); -} - -// }}} - -// {{{ server_nick_command - -auto server_nick_command::get_name() const noexcept -> std::string_view -{ - return "server-nick"; -} - -void server_nick_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!nick || nick->empty()) - throw server_error(server_error::invalid_nickname); - - bot.servers().require(*id)->set_nickname(*nick); - client.success("server-nick"); -} - -// }}} - -// {{{ server_notice_command - -auto server_notice_command::get_name() const noexcept -> std::string_view -{ - return "server-notice"; -} - -void server_notice_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!message) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->notice(*channel, *message); - client.success("server-notice"); -} - -// }}} - -// {{{ server_part_command - -auto server_part_command::get_name() const noexcept -> std::string_view -{ - return "server-part"; -} - -void server_part_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!reason) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->part(*channel, *reason); - client.success("server-part"); -} - -// }}} - -// {{{ server_reconnect_command - -auto server_reconnect_command::get_name() const noexcept -> std::string_view -{ - return "server-reconnect"; -} - -void server_reconnect_command::exec(bot& bot, transport_client& client, const document& args) -{ - const auto it = args.find("server"); - - if (it == args.end()) - bot.servers().reconnect(); - else { - if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) - throw server_error(server_error::invalid_identifier); - - bot.servers().reconnect(it->get<std::string>()); - } - - client.success("server-reconnect"); -} - -// }}} - -// {{{ server_topic_command - -auto server_topic_command::get_name() const noexcept -> std::string_view -{ - return "server-topic"; -} - -void server_topic_command::exec(bot& bot, transport_client& client, const document& args) -{ - 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); - if (!channel || channel->empty()) - throw server_error(server_error::invalid_channel); - if (!topic) - throw server_error(server_error::invalid_message); - - bot.servers().require(*id)->topic(*channel, *topic); - client.success("server-topic"); -} - -// }}} - -} // !irccd::daemon
--- a/libirccd-daemon/irccd/daemon/command.hpp Tue Jan 08 20:41:20 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,794 +0,0 @@ -/* - * command.hpp -- remote command - * - * Copyright (c) 2013-2019 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 - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_DAEMON_COMMAND_HPP -#define IRCCD_DAEMON_COMMAND_HPP - -/** - * \file command.hpp - * \brief Remote commands. - */ - -#include <irccd/sysconfig.hpp> - -#include <functional> -#include <memory> -#include <string_view> -#include <vector> - -#include <irccd/json_util.hpp> - -namespace irccd::daemon { - -class bot; -class transport_client; - -// {{{ command - -/** - * \brief Server side remote command - * \ingroup transports - */ -class command { -public: - /** - * \brief Convenient alias. - */ - using document = json_util::deserializer; - - /** - * \brief Command constructor factory. - */ - using constructor = std::function<std::unique_ptr<command> ()>; - - /** - * \brief Registry of all commands. - */ - static auto registry() noexcept -> const std::vector<constructor>&; - - /** - * Default destructor virtual. - */ - virtual ~command() = default; - - /** - * Return the command name, must not have spaces. - * - * \return the command name - */ - virtual auto get_name() const noexcept -> std::string_view = 0; - - /** - * Execute the command. - * - * If the command throw an exception, the error is sent to the client so be - * careful about sensitive information. - * - * The implementation should use client.success() or client.error() to send - * some data. - * - * \param bot the irccd instance - * \param client the client - * \param args the client arguments - */ - virtual void exec(bot& bot, transport_client& client, const document& args) = 0; -}; - -// }}} - -// {{{ plugin_config_command - -/** - * \brief Implementation of plugin-config transport command. - * \ingroup transports - * - * Replies: - * - * - plugin_error::not_found - */ -class plugin_config_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ plugin_info_command - -/** - * \brief Implementation of plugin-info transport command. - * \ingroup transports - * - * Replies: - * - * - plugin_error::not_found - */ -class plugin_info_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ plugin_list_command - -/** - * \brief Implementation of plugin-list transport command. - * \ingroup transports - */ -class plugin_list_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ plugin_load_command - -/** - * \brief Implementation of plugin-load transport command. - * \ingroup transports - * - * Replies: - * - * - plugin_error::already_exists - * - plugin_error::not_found - * - plugin_error::exec_error - */ -class plugin_load_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ plugin_reload_command - -/** - * \brief Implementation of plugin-reload transport command. - * \ingroup transports - * - * Replies: - * - * - plugin_error::not_found - * - plugin_error::exec_error - */ -class plugin_reload_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ plugin_unload_command - -/** - * \brief Implementation of plugin-unload transport command. - * \ingroup transports - * - * Replies: - * - * - plugin_error::not_found - * - plugin_error::exec_error - */ -class plugin_unload_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_add_command - -/** - * \brief Implementation of rule-add transport command. - * \ingroup transports - * - * Replies: - * - * - rule_error::invalid_action - */ -class rule_add_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_edit_command - -/** - * \brief Implementation of rule-edit transport command. - * \ingroup transports - * - * Replies: - * - * - rule_error::invalid_index - * - rule_error::invalid_action - */ -class rule_edit_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_info_command - -/** - * \brief Implementation of rule-info transport command. - * \ingroup transports - * - * Replies: - * - * - rule_error::invalid_index - */ -class rule_info_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_list_command - -/** - * \brief Implementation of rule-list transport command. - * \ingroup transports - */ -class rule_list_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_move_command - -/** - * \brief Implementation of rule-move transport command. - * \ingroup transports - * - * Replies: - * - * - rule_error::invalid_index - */ -class rule_move_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ rule_remove_command - -/** - * \brief Implementation of rule-remove transport command. - * \ingroup transports - * - * Replies: - * - * - rule_error::invalid_index - */ -class rule_remove_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_connect_command - -/** - * \brief Implementation of server-connect transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::already_exists, - * - server_error::invalid_hostname, - * - server_error::invalid_identifier, - * - server_error::invalid_port_number, - * - server_error::ssl_disabled. - */ -class server_connect_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_disconnect_command - -/** - * \brief Implementation of server-disconnect transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_disconnect_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_info_command - -/** - * \brief Implementation of server-info transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_info_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_invite_command - -/** - * \brief Implementation of server-invite transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::invalid_nickname, - * - server_error::not_found. - */ -class server_invite_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_join_command - -/** - * \brief Implementation of server-join transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_join_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_kick_command - -/** - * \brief Implementation of server-kick transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::invalid_nickname, - * - server_error::not_found. - */ -class server_kick_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_list_command - -/** - * \brief Implementation of server-list transport command. - * \ingroup transports - */ -class server_list_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_me_command - -/** - * \brief Implementation of server-me transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_me_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_message_command - -/** - * \brief Implementation of server-message transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_message_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_mode_command - -/** - * \brief Implementation of server-mode transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::invalid_mode, - * - server_error::not_found. - */ -class server_mode_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_nick_command - -/** - * \brief Implementation of server-nick transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_identifier, - * - server_error::invalid_nickname, - * - server_error::not_found. - */ -class server_nick_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_notice_command - -/** - * \brief Implementation of server-notice transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_notice_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_part_command - -/** - * \brief Implementation of server-part transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_part_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_reconnect_command - -/** - * \brief Implementation of server-reconnect transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_reconnect_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -// {{{ server_topic_command - -/** - * \brief Implementation of server-topic transport command. - * \ingroup transports - * - * Replies: - * - * - server_error::invalid_channel, - * - server_error::invalid_identifier, - * - server_error::not_found. - */ -class server_topic_command : public command { -public: - /** - * \copydoc command::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc command::exec - */ - void exec(bot& bot, transport_client& client, const document& args) override; -}; - -// }}} - -} // !irccd::daemon - -#endif // !IRCCD_DAEMON_COMMAND_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-daemon/irccd/daemon/transport_command.cpp Tue Jan 08 21:47:59 2019 +0100 @@ -0,0 +1,863 @@ +/* + * command.cpp -- remote command + * + * Copyright (c) 2013-2019 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/sysconfig.hpp> + +#include <irccd/string_util.hpp> + +#include "bot.hpp" +#include "plugin.hpp" +#include "plugin_service.hpp" +#include "rule.hpp" +#include "rule_service.hpp" +#include "rule_util.hpp" +#include "server.hpp" +#include "server_service.hpp" +#include "server_util.hpp" +#include "transport_client.hpp" +#include "transport_command.hpp" + +using namespace std::string_literals; + +namespace irccd::daemon { + +namespace { + +void exec_set(transport_client& client, plugin& plugin, const nlohmann::json& args) +{ + assert(args.count("value") > 0); + + const auto var = args.find("variable"); + const auto value = args.find("value"); + + if (var == args.end() || !var->is_string()) + throw bot_error(bot_error::error::incomplete_message); + if (value == args.end() || !value->is_string()) + throw bot_error(bot_error::error::incomplete_message); + + auto config = plugin.get_options(); + + config[var->get<std::string>()] = value->get<std::string>(); + plugin.set_options(config); + client.success("plugin-config"); +} + +void exec_get(transport_client& client, plugin& plugin, const nlohmann::json& args) +{ + auto variables = nlohmann::json::object(); + auto var = args.find("variable"); + + if (var != args.end() && var->is_string()) + variables[var->get<std::string>()] = plugin.get_options()[*var]; + else + for (const auto& pair : plugin.get_options()) + variables[pair.first] = pair.second; + + /* + * Don't put all variables into the response, put them into a sub + * property 'variables' instead. + * + * It's easier for the client to iterate over all. + */ + client.write({ + { "command", "plugin-config" }, + { "variables", variables } + }); +} + +template <typename T> +auto bind() noexcept -> transport_command::constructor +{ + return [] () noexcept { + return std::make_unique<T>(); + }; +} + +} // !namespace + +auto transport_command::registry() noexcept -> const std::vector<constructor>& +{ + static const std::vector<transport_command::constructor> list{ + bind<plugin_config_command>(), + bind<plugin_info_command>(), + bind<plugin_list_command>(), + bind<plugin_load_command>(), + bind<plugin_reload_command>(), + bind<plugin_unload_command>(), + bind<rule_add_command>(), + bind<rule_edit_command>(), + bind<rule_info_command>(), + bind<rule_info_command>(), + bind<rule_list_command>(), + bind<rule_move_command>(), + bind<rule_remove_command>(), + bind<server_connect_command>(), + bind<server_disconnect_command>(), + bind<server_info_command>(), + bind<server_invite_command>(), + bind<server_join_command>(), + bind<server_kick_command>(), + bind<server_list_command>(), + bind<server_me_command>(), + bind<server_message_command>(), + bind<server_mode_command>(), + bind<server_nick_command>(), + bind<server_notice_command>(), + bind<server_part_command>(), + bind<server_reconnect_command>(), + bind<server_topic_command>() + }; + + return list; +}; + +// {{{ plugin_config_command + +auto plugin_config_command::get_name() const noexcept -> std::string_view +{ + return "plugin-config"; +} + +void plugin_config_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("plugin"); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + const auto plugin = bot.plugins().require(*id); + + if (args.count("value") > 0) + exec_set(client, *plugin, args); + else + exec_get(client, *plugin, args); +} + +// }}} + +// {{{ plugin_info_command + +auto plugin_info_command::get_name() const noexcept -> std::string_view +{ + return "plugin-info"; +} + +void plugin_info_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("plugin"); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + const auto plugin = bot.plugins().require(*id); + + client.write({ + { "command", "plugin-info" }, + { "author", std::string(plugin->get_author()) }, + { "license", std::string(plugin->get_license()) }, + { "summary", std::string(plugin->get_summary()) }, + { "version", std::string(plugin->get_version()) } + }); +} + +// }}} + +// {{{ plugin_list_command + +auto plugin_list_command::get_name() const noexcept -> std::string_view +{ + return "plugin-list"; +} + +void plugin_list_command::exec(bot& bot, transport_client& client, const document&) +{ + auto list = nlohmann::json::array(); + + for (const auto& plg : bot.plugins().list()) + list += plg->get_id(); + + client.write({ + { "command", "plugin-list" }, + { "list", list } + }); +} + +// }}} + +// {{{ plugin_load_command + +auto plugin_load_command::get_name() const noexcept -> std::string_view +{ + return "plugin-load"; +} + +void plugin_load_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("plugin"); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + bot.plugins().load(*id, ""); + client.success("plugin-load"); +} + +// }}} + +// {{{ plugin_reload_command + +auto plugin_reload_command::get_name() const noexcept -> std::string_view +{ + return "plugin-reload"; +} + +void plugin_reload_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("plugin"); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + bot.plugins().reload(*id); + client.success("plugin-reload"); +} + +// }}} + +// {{{ plugin_unload_command + +auto plugin_unload_command::get_name() const noexcept -> std::string_view +{ + return "plugin-unload"; +} + +void plugin_unload_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("plugin"); + + if (!id || !string_util::is_identifier(*id)) + throw plugin_error(plugin_error::invalid_identifier); + + bot.plugins().unload(*id); + client.success("plugin-unload"); +} + +// }}} + +// {{{ rule_add_command + +auto rule_add_command::get_name() const noexcept -> std::string_view +{ + return "rule-add"; +} + +void rule_add_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto index = args.optional<unsigned>("index", bot.rules().list().size()); + + if (!index || *index > bot.rules().list().size()) + throw rule_error(rule_error::error::invalid_index); + + bot.rules().insert(rule_util::from_json(args), *index); + client.success("rule-add"); +} + +// }}} + +// {{{ rule_edit_command + +auto rule_edit_command::get_name() const noexcept -> std::string_view +{ + return "rule-edit"; +} + +void rule_edit_command::exec(bot& bot, 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]) { + if (v.is_string()) + set.erase(v.template get<std::string>()); + } + for (const auto& v : args["add-"s + key]) { + if (v.is_string()) + set.insert(v.template get<std::string>()); + } + }; + + const auto index = args.get<unsigned>("index"); + + if (!index) + throw rule_error(rule_error::invalid_index); + + // Create a copy to avoid incomplete edition in case of errors. + auto rule = bot.rules().require(*index); + + updateset(rule.channels, args, "channels"); + updateset(rule.events, args, "events"); + updateset(rule.plugins, args, "plugins"); + updateset(rule.servers, args, "servers"); + + auto action = args.find("action"); + + if (action != args.end()) { + if (!action->is_string()) + throw rule_error(rule_error::error::invalid_action); + + if (action->get<std::string>() == "accept") + rule.action = rule::action_type::accept; + else if (action->get<std::string>() == "drop") + rule.action = rule::action_type::drop; + else + throw rule_error(rule_error::invalid_action); + } + + // All done, sync the rule. + bot.rules().require(*index) = rule; + client.success("rule-edit"); +} + +// }}} + +// {{{ rule_info_command + +auto rule_info_command::get_name() const noexcept -> std::string_view +{ + return "rule-info"; +} + +void rule_info_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto index = args.get<unsigned>("index"); + + if (!index) + throw rule_error(rule_error::invalid_index); + + auto json = rule_util::to_json(bot.rules().require(*index)); + + json.push_back({"command", "rule-info"}); + client.write(std::move(json)); +} + +// }}} + +// {{{ rule_list_command + +auto rule_list_command::get_name() const noexcept -> std::string_view +{ + return "rule-list"; +} + +void rule_list_command::exec(bot& bot, transport_client& client, const document&) +{ + auto array = nlohmann::json::array(); + + for (const auto& rule : bot.rules().list()) + array.push_back(rule_util::to_json(rule)); + + client.write({ + { "command", "rule-list" }, + { "list", std::move(array) } + }); +} + +// }}} + +// {{{ rule_move_command + +auto rule_move_command::get_name() const noexcept -> std::string_view +{ + return "rule-move"; +} + +void rule_move_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto from = args.get<unsigned>("from"); + const auto to = args.get<unsigned>("to"); + + if (!from || !to) + throw rule_error(rule_error::invalid_index); + + /* + * Examples of moves + * -------------------------------------------------------------- + * + * Before: [0] [1] [2] + * + * from = 0 + * to = 2 + * + * After: [1] [2] [0] + * + * -------------------------------------------------------------- + * + * Before: [0] [1] [2] + * + * from = 2 + * to = 0 + * + * After: [2] [0] [1] + * + * -------------------------------------------------------------- + * + * Before: [0] [1] [2] + * + * from = 0 + * to = 123 + * + * After: [1] [2] [0] + */ + + // Ignore dumb input. + if (*from == *to) { + client.success("rule-move"); + return; + } + + if (*from >= bot.rules().list().size()) + throw rule_error(rule_error::error::invalid_index); + + const auto save = bot.rules().list()[*from]; + + bot.rules().remove(*from); + bot.rules().insert(save, *to > bot.rules().list().size() ? bot.rules().list().size() : *to); + client.success("rule-move"); +} + +// }}} + +// {{{ rule_remove_command + +auto rule_remove_command::get_name() const noexcept -> std::string_view +{ + return "rule-remove"; +} + +void rule_remove_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto index = args.get<unsigned>("index"); + + if (!index || *index >= bot.rules().list().size()) + throw rule_error(rule_error::invalid_index); + + bot.rules().remove(*index); + client.success("rule-remove"); +} + +// }}} + +// {{{ server_connect_command + +auto server_connect_command::get_name() const noexcept -> std::string_view +{ + return "server-connect"; +} + +void server_connect_command::exec(bot& bot, transport_client& client, const document& args) +{ + auto server = server_util::from_json(bot.get_service(), args); + + if (bot.servers().has(server->get_id())) + throw server_error(server_error::already_exists); + + bot.servers().add(std::move(server)); + client.success("server-connect"); +} + +// }}} + +// {{{ server_disconnect_command + +auto server_disconnect_command::get_name() const noexcept -> std::string_view +{ + return "server-disconnect"; +} + +void server_disconnect_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto it = args.find("server"); + + if (it == args.end()) + bot.servers().clear(); + else { + if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) + throw server_error(server_error::invalid_identifier); + + const auto name = it->get<std::string>(); + + bot.servers().require(name); + bot.servers().remove(name); + } + + client.success("server-disconnect"); +} + +// }}} + +// {{{ server_info_command + +auto server_info_command::get_name() const noexcept -> std::string_view +{ + return "server-info"; +} + +void server_info_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto id = args.get<std::string>("server"); + + if (!id || !string_util::is_identifier(*id)) + throw server_error(server_error::invalid_identifier); + + const auto server = bot.servers().require(*id); + + // Construct the JSON response. + auto response = document::object(); + + // General stuff. + response.push_back({"command", "server-info"}); + response.push_back({"name", server->get_id()}); + response.push_back({"hostname", server->get_hostname()}); + response.push_back({"port", server->get_port()}); + response.push_back({"nickname", server->get_nickname()}); + response.push_back({"username", server->get_username()}); + response.push_back({"realname", server->get_realname()}); + response.push_back({"channels", server->get_channels()}); + + // Optional stuff. + response.push_back({"ipv4", static_cast<bool>(server->get_options() & server::options::ipv4)}); + response.push_back({"ipv6", static_cast<bool>(server->get_options() & server::options::ipv6)}); + response.push_back({"ssl", static_cast<bool>(server->get_options() & server::options::ssl)}); + + client.write(response); +} + +// }}} + +// {{{ server_invite_command + +auto server_invite_command::get_name() const noexcept -> std::string_view +{ + return "server-invite"; +} + +void server_invite_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!target || target->empty()) + throw server_error(server_error::invalid_nickname); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + + bot.servers().require(*id)->invite(*target, *channel); + client.success("server-invite"); +} + +// }}} + +// {{{ server_join_command + +auto server_join_command::get_name() const noexcept -> std::string_view +{ + return "server-join"; +} + +void server_join_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!password) + throw server_error(server_error::invalid_password); + + bot.servers().require(*id)->join(*channel, *password); + client.success("server-join"); +} + +// }}} + +// {{{ server_kick_command + +auto server_kick_command::get_name() const noexcept -> std::string_view +{ + return "server-kick"; +} + +void server_kick_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!target || target->empty()) + throw server_error(server_error::invalid_nickname); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!reason) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->kick(*target, *channel, *reason); + client.success("server-kick"); +} + +// }}} + +// {{{ server_list_command + +auto server_list_command::get_name() const noexcept -> std::string_view +{ + return "server-list"; +} + +void server_list_command::exec(bot& bot, transport_client& client, const document&) +{ + auto json = nlohmann::json::object(); + auto list = nlohmann::json::array(); + + for (const auto& server : bot.servers().list()) + list.push_back(server->get_id()); + + client.write({ + { "command", "server-list" }, + { "list", std::move(list) } + }); +} + +// }}} + +// {{{ server_me_command + +auto server_me_command::get_name() const noexcept -> std::string_view +{ + return "server-me"; +} + +void server_me_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!message) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->me(*channel, *message); + client.success("server-me"); +} + +// }}} + +// {{{ server_message_command + +auto server_message_command::get_name() const noexcept -> std::string_view +{ + return "server-message"; +} + +void server_message_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!message) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->message(*channel, *message); + client.success("server-message"); +} + +// }}} + +// {{{ server_mode_command + +auto server_mode_command::get_name() const noexcept -> std::string_view +{ + return "server-mode"; +} + +void server_mode_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!mode || mode->empty()) + throw server_error(server_error::invalid_mode); + if (!limit || !user || !mask) + throw server_error(server_error::invalid_mode); + + bot.servers().require(*id)->mode(*channel, *mode, *limit, *user, *mask); + client.success("server-mode"); +} + +// }}} + +// {{{ server_nick_command + +auto server_nick_command::get_name() const noexcept -> std::string_view +{ + return "server-nick"; +} + +void server_nick_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!nick || nick->empty()) + throw server_error(server_error::invalid_nickname); + + bot.servers().require(*id)->set_nickname(*nick); + client.success("server-nick"); +} + +// }}} + +// {{{ server_notice_command + +auto server_notice_command::get_name() const noexcept -> std::string_view +{ + return "server-notice"; +} + +void server_notice_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!message) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->notice(*channel, *message); + client.success("server-notice"); +} + +// }}} + +// {{{ server_part_command + +auto server_part_command::get_name() const noexcept -> std::string_view +{ + return "server-part"; +} + +void server_part_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!reason) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->part(*channel, *reason); + client.success("server-part"); +} + +// }}} + +// {{{ server_reconnect_command + +auto server_reconnect_command::get_name() const noexcept -> std::string_view +{ + return "server-reconnect"; +} + +void server_reconnect_command::exec(bot& bot, transport_client& client, const document& args) +{ + const auto it = args.find("server"); + + if (it == args.end()) + bot.servers().reconnect(); + else { + if (!it->is_string() || !string_util::is_identifier(it->get<std::string>())) + throw server_error(server_error::invalid_identifier); + + bot.servers().reconnect(it->get<std::string>()); + } + + client.success("server-reconnect"); +} + +// }}} + +// {{{ server_topic_command + +auto server_topic_command::get_name() const noexcept -> std::string_view +{ + return "server-topic"; +} + +void server_topic_command::exec(bot& bot, transport_client& client, const document& args) +{ + 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); + if (!channel || channel->empty()) + throw server_error(server_error::invalid_channel); + if (!topic) + throw server_error(server_error::invalid_message); + + bot.servers().require(*id)->topic(*channel, *topic); + client.success("server-topic"); +} + +// }}} + +} // !irccd::daemon
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-daemon/irccd/daemon/transport_command.hpp Tue Jan 08 21:47:59 2019 +0100 @@ -0,0 +1,794 @@ +/* + * transport_command.hpp -- remote command + * + * Copyright (c) 2013-2019 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_DAEMON_TRANSPORT_COMMAND_HPP +#define IRCCD_DAEMON_TRANSPORT_COMMAND_HPP + +/** + * \file transport_command.hpp + * \brief Remote commands. + */ + +#include <irccd/sysconfig.hpp> + +#include <functional> +#include <memory> +#include <string_view> +#include <vector> + +#include <irccd/json_util.hpp> + +namespace irccd::daemon { + +class bot; +class transport_client; + +// {{{ transport_command + +/** + * \brief Server side remote command + * \ingroup transports + */ +class transport_command { +public: + /** + * \brief Convenient alias. + */ + using document = json_util::deserializer; + + /** + * \brief Command constructor factory. + */ + using constructor = std::function<std::unique_ptr<transport_command> ()>; + + /** + * \brief Registry of all commands. + */ + static auto registry() noexcept -> const std::vector<constructor>&; + + /** + * Default destructor virtual. + */ + virtual ~transport_command() = default; + + /** + * Return the command name, must not have spaces. + * + * \return the command name + */ + virtual auto get_name() const noexcept -> std::string_view = 0; + + /** + * Execute the command. + * + * If the command throw an exception, the error is sent to the client so be + * careful about sensitive information. + * + * The implementation should use client.success() or client.error() to send + * some data. + * + * \param bot the irccd instance + * \param client the client + * \param args the client arguments + */ + virtual void exec(bot& bot, transport_client& client, const document& args) = 0; +}; + +// }}} + +// {{{ plugin_config_command + +/** + * \brief Implementation of plugin-config transport command. + * \ingroup transports + * + * Replies: + * + * - plugin_error::not_found + */ +class plugin_config_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ plugin_info_command + +/** + * \brief Implementation of plugin-info transport command. + * \ingroup transports + * + * Replies: + * + * - plugin_error::not_found + */ +class plugin_info_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ plugin_list_command + +/** + * \brief Implementation of plugin-list transport command. + * \ingroup transports + */ +class plugin_list_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ plugin_load_command + +/** + * \brief Implementation of plugin-load transport command. + * \ingroup transports + * + * Replies: + * + * - plugin_error::already_exists + * - plugin_error::not_found + * - plugin_error::exec_error + */ +class plugin_load_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ plugin_reload_command + +/** + * \brief Implementation of plugin-reload transport command. + * \ingroup transports + * + * Replies: + * + * - plugin_error::not_found + * - plugin_error::exec_error + */ +class plugin_reload_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ plugin_unload_command + +/** + * \brief Implementation of plugin-unload transport command. + * \ingroup transports + * + * Replies: + * + * - plugin_error::not_found + * - plugin_error::exec_error + */ +class plugin_unload_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_add_command + +/** + * \brief Implementation of rule-add transport command. + * \ingroup transports + * + * Replies: + * + * - rule_error::invalid_action + */ +class rule_add_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_edit_command + +/** + * \brief Implementation of rule-edit transport command. + * \ingroup transports + * + * Replies: + * + * - rule_error::invalid_index + * - rule_error::invalid_action + */ +class rule_edit_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_info_command + +/** + * \brief Implementation of rule-info transport command. + * \ingroup transports + * + * Replies: + * + * - rule_error::invalid_index + */ +class rule_info_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_list_command + +/** + * \brief Implementation of rule-list transport command. + * \ingroup transports + */ +class rule_list_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_move_command + +/** + * \brief Implementation of rule-move transport command. + * \ingroup transports + * + * Replies: + * + * - rule_error::invalid_index + */ +class rule_move_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ rule_remove_command + +/** + * \brief Implementation of rule-remove transport command. + * \ingroup transports + * + * Replies: + * + * - rule_error::invalid_index + */ +class rule_remove_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_connect_command + +/** + * \brief Implementation of server-connect transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::already_exists, + * - server_error::invalid_hostname, + * - server_error::invalid_identifier, + * - server_error::invalid_port_number, + * - server_error::ssl_disabled. + */ +class server_connect_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_disconnect_command + +/** + * \brief Implementation of server-disconnect transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_disconnect_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_info_command + +/** + * \brief Implementation of server-info transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_info_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_invite_command + +/** + * \brief Implementation of server-invite transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::invalid_nickname, + * - server_error::not_found. + */ +class server_invite_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_join_command + +/** + * \brief Implementation of server-join transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_join_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_kick_command + +/** + * \brief Implementation of server-kick transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::invalid_nickname, + * - server_error::not_found. + */ +class server_kick_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_list_command + +/** + * \brief Implementation of server-list transport command. + * \ingroup transports + */ +class server_list_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_me_command + +/** + * \brief Implementation of server-me transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_me_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_message_command + +/** + * \brief Implementation of server-message transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_message_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_mode_command + +/** + * \brief Implementation of server-mode transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::invalid_mode, + * - server_error::not_found. + */ +class server_mode_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_nick_command + +/** + * \brief Implementation of server-nick transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_identifier, + * - server_error::invalid_nickname, + * - server_error::not_found. + */ +class server_nick_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_notice_command + +/** + * \brief Implementation of server-notice transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_notice_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_part_command + +/** + * \brief Implementation of server-part transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_part_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_reconnect_command + +/** + * \brief Implementation of server-reconnect transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_reconnect_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +// {{{ server_topic_command + +/** + * \brief Implementation of server-topic transport command. + * \ingroup transports + * + * Replies: + * + * - server_error::invalid_channel, + * - server_error::invalid_identifier, + * - server_error::not_found. + */ +class server_topic_command : public transport_command { +public: + /** + * \copydoc command::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc command::exec + */ + void exec(bot& bot, transport_client& client, const document& args) override; +}; + +// }}} + +} // !irccd::daemon + +#endif // !IRCCD_DAEMON_COMMAND_HPP
--- a/libirccd-daemon/irccd/daemon/transport_service.cpp Tue Jan 08 20:41:20 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_service.cpp Tue Jan 08 21:47:59 2019 +0100 @@ -23,9 +23,9 @@ #include <irccd/json_util.hpp> #include "bot.hpp" -#include "command.hpp" #include "logger.hpp" #include "transport_client.hpp" +#include "transport_command.hpp" #include "transport_server.hpp" #include "transport_service.hpp" #include "transport_util.hpp"
--- a/libirccd-daemon/irccd/daemon/transport_service.hpp Tue Jan 08 20:41:20 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_service.hpp Tue Jan 08 21:47:59 2019 +0100 @@ -38,8 +38,8 @@ namespace daemon { class bot; -class command; class transport_client; +class transport_command; class transport_server; /** @@ -52,7 +52,7 @@ /** * \brief the list of transport commands. */ - using commands = std::vector<std::unique_ptr<command>>; + using commands = std::vector<std::unique_ptr<transport_command>>; /** * \brief The list of transport acceptors.
--- a/libirccd-test/irccd/test/cli_fixture.cpp Tue Jan 08 20:41:20 2019 +0100 +++ b/libirccd-test/irccd/test/cli_fixture.cpp Tue Jan 08 21:47:59 2019 +0100 @@ -24,7 +24,7 @@ #include <irccd/string_util.hpp> #include <irccd/acceptor.hpp> -#include <irccd/daemon/command.hpp> +#include <irccd/daemon/transport_command.hpp> #include <irccd/daemon/transport_service.hpp> #include <irccd/daemon/transport_server.hpp> @@ -33,10 +33,6 @@ namespace proc = boost::process; -using irccd::daemon::bot; -using irccd::daemon::command; -using irccd::daemon::transport_server; - namespace irccd::test { namespace { @@ -64,11 +60,11 @@ auto acceptor = std::make_unique<ip_acceptor>(bot_.get_service(), std::move(raw_acceptor)); - for (const auto& f : command::registry()) + for (const auto& f : daemon::transport_command::registry()) bot_.transports().get_commands().push_back(f()); bot_.servers().add(server_); - bot_.transports().add(std::make_unique<transport_server>(std::move(acceptor))); + bot_.transports().add(std::make_unique<daemon::transport_server>(std::move(acceptor))); bot_.plugins().add_loader(std::make_unique<test_plugin_loader>()); server_->clear(); }
--- a/libirccd-test/irccd/test/command_fixture.cpp Tue Jan 08 20:41:20 2019 +0100 +++ b/libirccd-test/irccd/test/command_fixture.cpp Tue Jan 08 21:47:59 2019 +0100 @@ -19,23 +19,18 @@ #include <irccd/acceptor.hpp> #include <irccd/connector.hpp> -#include <irccd/daemon/command.hpp> +#include <irccd/daemon/transport_command.hpp> #include <irccd/daemon/transport_server.hpp> #include <irccd/daemon/transport_service.hpp> #include "command_fixture.hpp" -using boost::asio::ip::tcp; -using boost::asio::deadline_timer; - -using boost::posix_time::seconds; - -using irccd::daemon::command; -using irccd::daemon::transport_server; +namespace asio = boost::asio; +namespace posix_time = boost::posix_time; namespace irccd::test { -auto command_fixture::recv(deadline_timer& timer) -> result +auto command_fixture::recv(asio::deadline_timer& timer) -> result { result r; @@ -57,11 +52,11 @@ auto command_fixture::wait_command(const std::string& cmd) -> result { result r; - deadline_timer timer(bot_.get_service()); + asio::deadline_timer timer(bot_.get_service()); - timer.expires_from_now(seconds(30)); + timer.expires_from_now(posix_time::seconds(30)); timer.async_wait([] (auto code) { - if (code != boost::asio::error::operation_aborted) + if (code != asio::error::operation_aborted) throw std::runtime_error("operation timed out"); }); @@ -96,27 +91,27 @@ : server_(new mock_server(ctx_, "test", "localhost")) , plugin_(new mock_plugin("test")) { - tcp::endpoint ep(tcp::v4(), 0U); - tcp::acceptor raw_acceptor(bot_.get_service(), std::move(ep)); + asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 0U); + asio::ip::tcp::acceptor raw_acceptor(bot_.get_service(), std::move(ep)); auto service = std::to_string(raw_acceptor.local_endpoint().port()); auto acceptor = std::make_unique<ip_acceptor>(bot_.get_service(), std::move(raw_acceptor)); auto connector = std::make_unique<ip_connector>(bot_.get_service(), "127.0.0.1", service, true, false); // 1. Add all commands. - for (const auto& f : command::registry()) + for (const auto& f : daemon::transport_command::registry()) bot_.transports().get_commands().push_back(f()); // 2. Create controller and transport server. ctl_ = std::make_unique<ctl::controller>(std::move(connector)); - bot_.transports().add(std::make_unique<transport_server>(std::move(acceptor))); + bot_.transports().add(std::make_unique<daemon::transport_server>(std::move(acceptor))); // 3. Wait for controller to connect. - boost::asio::deadline_timer timer(ctx_); + asio::deadline_timer timer(ctx_); - timer.expires_from_now(boost::posix_time::seconds(10)); + timer.expires_from_now(posix_time::seconds(10)); timer.async_wait([] (auto code) { - if (code && code != boost::asio::error::operation_aborted) + if (code && code != asio::error::operation_aborted) throw std::system_error(make_error_code(std::errc::timed_out)); });