Mercurial > irccd
changeset 291:b490853404d9
Irccd: split lib into libirccd, #564
line wrap: on
line diff
--- a/CMakeLists.txt Wed Oct 05 13:06:00 2016 +0200 +++ b/CMakeLists.txt Wed Oct 05 13:27:15 2016 +0200 @@ -79,6 +79,7 @@ add_subdirectory(extern/json) add_subdirectory(doc) add_subdirectory(libcommon) +add_subdirectory(libirccd) #add_subdirectory(lib) #add_subdirectory(irccd) #add_subdirectory(irccdctl)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/CMakeLists.txt Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,92 @@ +project(libirccd) + +set( + HEADERS + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-config.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-info.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-list.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-load.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-reload.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-unload.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-cmode.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-cnotice.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-connect.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-disconnect.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-info.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-invite.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-join.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-kick.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-list.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-me.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-message.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-mode.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-nick.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-notice.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-part.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-reconnect.hpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-topic.hpp + ${libirccd_SOURCE_DIR}/irccd/command.hpp + ${libirccd_SOURCE_DIR}/irccd/config.hpp + ${libirccd_SOURCE_DIR}/irccd/dynlib.hpp + ${libirccd_SOURCE_DIR}/irccd/irccd.hpp + ${libirccd_SOURCE_DIR}/irccd/plugin-dynlib.hpp + ${libirccd_SOURCE_DIR}/irccd/plugin.hpp + ${libirccd_SOURCE_DIR}/irccd/rule.hpp + ${libirccd_SOURCE_DIR}/irccd/server.hpp + ${libirccd_SOURCE_DIR}/irccd/service-command.hpp + ${libirccd_SOURCE_DIR}/irccd/service-interrupt.hpp + ${libirccd_SOURCE_DIR}/irccd/service-plugin.hpp + ${libirccd_SOURCE_DIR}/irccd/service-rule.hpp + ${libirccd_SOURCE_DIR}/irccd/service-server.hpp + ${libirccd_SOURCE_DIR}/irccd/service-transport.hpp + ${libirccd_SOURCE_DIR}/irccd/transport.hpp +) + +set( + SOURCES + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-config.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-info.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-list.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-load.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-reload.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-plugin-unload.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-cmode.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-cnotice.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-connect.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-disconnect.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-info.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-invite.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-join.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-kick.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-list.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-me.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-message.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-mode.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-nick.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-notice.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-part.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-reconnect.cpp + ${libirccd_SOURCE_DIR}/irccd/cmd-server-topic.cpp + ${libirccd_SOURCE_DIR}/irccd/command.cpp + ${libirccd_SOURCE_DIR}/irccd/config.cpp + ${libirccd_SOURCE_DIR}/irccd/irccd.cpp + ${libirccd_SOURCE_DIR}/irccd/plugin-dynlib.cpp + ${libirccd_SOURCE_DIR}/irccd/rule.cpp + ${libirccd_SOURCE_DIR}/irccd/server.cpp + ${libirccd_SOURCE_DIR}/irccd/service-command.cpp + ${libirccd_SOURCE_DIR}/irccd/service-interrupt.cpp + ${libirccd_SOURCE_DIR}/irccd/service-plugin.cpp + ${libirccd_SOURCE_DIR}/irccd/service-rule.cpp + ${libirccd_SOURCE_DIR}/irccd/service-server.cpp + ${libirccd_SOURCE_DIR}/irccd/service-transport.cpp + ${libirccd_SOURCE_DIR}/irccd/transport.cpp +) + +irccd_define_library( + TARGET libirccd + SOURCES + ${libirccd_SOURCE_DIR}/CMakeLists.txt + ${HEADERS} + ${SOURCES} + LIBRARIES extern-ircclient libcommon +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-config.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,131 @@ +/* + * cmd-plugin-config.cpp -- implementation of plugin-config command + * + * Copyright (c) 2013-2016 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 <iomanip> +#include <iostream> + +#include "irccd.hpp" +#include "cmd-plugin-config.hpp" +#include "service-plugin.hpp" + +namespace irccd { + +namespace command { + +namespace { + +nlohmann::json execSet(Irccd &irccd, const nlohmann::json &request, const std::string &var, const std::string &value) +{ + auto plugin = irccd.plugins().require(request["plugin"].get<std::string>()); + auto config = plugin->config(); + + config[var] = value; + plugin->setConfig(config); + + return nullptr; +} + +nlohmann::json execGet(Irccd &irccd, const nlohmann::json &request, const nlohmann::json::const_iterator &var) +{ + auto config = irccd.plugins().require(request["plugin"].get<std::string>())->config(); + + // 'vars' property. + std::map<std::string, nlohmann::json> vars; + + if (var != request.end()) + vars.emplace(var->get<std::string>(), config[var->get<std::string>()]); + else + for (const auto &pair : config) + vars.emplace(pair.first, pair.second); + + return nlohmann::json::object({{ "variables", nlohmann::json(vars) }}); +} + +} // !namespace + +PluginConfigCommand::PluginConfigCommand() + : Command("plugin-config", "Plugins", "Get or set a plugin config variable") +{ +} + +std::vector<Command::Arg> PluginConfigCommand::args() const +{ + return { + { "plugin", true }, + { "variable", false }, + { "value", false } + }; +} + +std::vector<Command::Property> PluginConfigCommand::properties() const +{ + return {{ "plugin", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json PluginConfigCommand::request(Irccdctl &, const CommandRequest &args) const +{ + auto object = nlohmann::json::object({ + { "plugin", args.arg(0) } + }); + + if (args.length() >= 2U) { + object.push_back({"variable", args.arg(1)}); + + if (args.length() == 3U) + object.push_back({"value", args.arg(2)}); + } + + return object; +} + +nlohmann::json PluginConfigCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + auto var = request.find("variable"); + + if (var != request.end() && var->is_string()) + throw InvalidPropertyError("variable", nlohmann::json::value_t::string, var->type()); + + auto value = request.find("value"); + + if (value != request.end()) + return execSet(irccd, request, var->dump(), value->dump()); + + return execGet(irccd, request, var); +} + +void PluginConfigCommand::result(Irccdctl &irccdctl, const nlohmann::json &response) const +{ + Command::result(irccdctl, response); + + auto it = response.find("variables"); + + if (it == response.end() || !it->is_object()) + return; + + if (it->size() > 1U) + for (auto v = it->begin(); v != it->end(); ++v) + std::cout << std::setw(16) << std::left << v.key() << " : " << v->dump() << std::endl; + else + std::cout << it->begin()->dump() << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-config.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,73 @@ +/* + * cmd-plugin-config.hpp -- implementation of plugin-config command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_CONFIG_HPP +#define IRCCD_CMD_PLUGIN_CONFIG_HPP + +/** + * \file cmd-plugin-config.hpp + * \brief Implementation of plugin-config transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-config transport command. + */ +class PluginConfigCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginConfigCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; + + /** + * \copydoc Command::result + */ + IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_CONFIG_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-info.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,93 @@ +/* + * cmd-plugin-info.cpp -- implementation of plugin-info command + * + * Copyright (c) 2013-2016 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 <iostream> + +#include "cmd-plugin-info.hpp" +#include "irccd.hpp" +#include "plugin.hpp" +#include "service-plugin.hpp" +#include "util.hpp" + +namespace irccd { + +namespace command { + +PluginInfoCommand::PluginInfoCommand() + : Command("plugin-info", "Plugins", "Get plugin information") +{ +} + +std::vector<Command::Arg> PluginInfoCommand::args() const +{ + return {{ "plugin", true }}; +} + +std::vector<Command::Property> PluginInfoCommand::properties() const +{ + return {{ "plugin", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json PluginInfoCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({{ "plugin", args.arg(0) }}); +} + +nlohmann::json PluginInfoCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + auto plugin = irccd.plugins().require(request.at("plugin").get<std::string>()); + + return nlohmann::json::object({ + { "author", plugin->author() }, + { "license", plugin->license() }, + { "summary", plugin->summary() }, + { "version", plugin->version() } + }); +} + +void PluginInfoCommand::result(Irccdctl &irccdctl, const nlohmann::json &result) const +{ + Command::result(irccdctl, result); + + auto it = result.find("status"); + + if (!it->is_boolean() || !*it) + return; + + auto get = [&] (auto key) -> std::string { + auto v = result.find(key); + + if (v == result.end() || !v->is_primitive()) + return ""; + + return v->dump(); + }; + + std::cout << std::boolalpha; + std::cout << "Author : " << get("author") << std::endl; + std::cout << "License : " << get("license") << std::endl; + std::cout << "Summary : " << get("summary") << std::endl; + std::cout << "Version : " << get("version") << std::endl; +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-info.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,73 @@ +/* + * cmd-plugin-info.hpp -- implementation of plugin-info command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_INFO_HPP +#define IRCCD_CMD_PLUGIN_INFO_HPP + +/** + * \file cmd-plugin-info.hpp + * \brief Implementation of plugin-info transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-info transport command. + */ +class PluginInfoCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginInfoCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; + + /** + * \copydoc Command::result + */ + IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_INFO_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-list.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,62 @@ +/* + * cmd-plugin-list.cpp -- implementation of plugin-list transport command + * + * Copyright (c) 2013-2016 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 <iostream> + +#include "cmd-plugin-list.hpp" +#include "irccd.hpp" +#include "plugin.hpp" +#include "service-plugin.hpp" +#include "util.hpp" + +namespace irccd { + +namespace command { + +PluginListCommand::PluginListCommand() + : Command("plugin-list", "Plugins", "Get the list of loaded plugins") +{ +} + +nlohmann::json PluginListCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + auto response = Command::exec(irccd, request); + auto list = nlohmann::json::array(); + + for (const auto &plugin : irccd.plugins().list()) + list += plugin->name(); + + response.push_back({"list", std::move(list)}); + + return response; +} + +void PluginListCommand::result(Irccdctl &irccdctl, const nlohmann::json &object) const +{ + Command::result(irccdctl, object); + + auto it = object.find("list"); + + if (it != object.end() && it->is_array()) + for (const auto &n : *it) + std::cout << n.dump() << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-list.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,58 @@ +/* + * cmd-plugin-list.hpp -- implementation of plugin-list transport command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_LIST_HPP +#define IRCCD_CMD_PLUGIN_LIST_HPP + +/** + * \file cmd-plugin-list.hpp + * \brief Implementation of plugin-list transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-list transport command. + */ +class PluginListCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginListCommand(); + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; + + /** + * \copydoc Command::result + */ + IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_LIST_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-load.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,59 @@ +/* + * cmd-plugin-load.cpp -- implementation of plugin-load transport command + * + * Copyright (c) 2013-2016 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 "cmd-plugin-load.hpp" +#include "irccd.hpp" +#include "service-plugin.hpp" +#include "util.hpp" + +namespace irccd { + +namespace command { + +PluginLoadCommand::PluginLoadCommand() + : Command("plugin-load", "Plugins", "Load a plugin") +{ +} + +std::vector<Command::Arg> PluginLoadCommand::args() const +{ + return {{ "plugin", true }}; +} + +std::vector<Command::Property> PluginLoadCommand::properties() const +{ + return {{ "plugin", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json PluginLoadCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({{ "plugin", args.arg(0) }}); +} + +nlohmann::json PluginLoadCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.plugins().load(request["plugin"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-load.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-plugin-load.hpp -- implementation of plugin-load transport command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_LOAD_HPP +#define IRCCD_CMD_PLUGIN_LOAD_HPP + +/** + * \file cmd-plugin-load.hpp + * \brief Implementation of plugin-load transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-load transport command. + */ +class PluginLoadCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginLoadCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_LOAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-reload.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,60 @@ +/* + * cmd-plugin-reload.cpp -- implementation of plugin-reload transport command + * + * Copyright (c) 2013-2016 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 "cmd-plugin-reload.hpp" +#include "irccd.hpp" +#include "plugin.hpp" +#include "service-plugin.hpp" +#include "util.hpp" + +namespace irccd { + +namespace command { + +PluginReloadCommand::PluginReloadCommand() + : Command("plugin-reload", "Plugins", "Reload a plugin") +{ +} + +std::vector<Command::Arg> PluginReloadCommand::args() const +{ + return {{ "plugin", true }}; +} + +std::vector<Command::Property> PluginReloadCommand::properties() const +{ + return {{ "plugin", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json PluginReloadCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({{ "plugin", args.arg(0) }}); +} + +nlohmann::json PluginReloadCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.plugins().require(request["plugin"])->onReload(irccd); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-reload.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-plugin-reload.hpp -- implementation of plugin-reload transport command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_RELOAD_HPP +#define IRCCD_CMD_PLUGIN_RELOAD_HPP + +/** + * \file cmd-plugin-reload.hpp + * \brief Implementation of plugin-reload transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-reload transport command. + */ +class PluginReloadCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginReloadCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_RELOAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-unload.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,59 @@ +/* + * cmd-plugin-unload.cpp -- implementation of plugin-unload transport command + * + * Copyright (c) 2013-2016 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 "cmd-plugin-unload.hpp" +#include "irccd.hpp" +#include "service-plugin.hpp" +#include "util.hpp" + +namespace irccd { + +namespace command { + +PluginUnloadCommand::PluginUnloadCommand() + : Command("plugin-unload", "Plugins", "Unload a plugin") +{ +} + +std::vector<Command::Arg> PluginUnloadCommand::args() const +{ + return {{ "plugin", true }}; +} + +std::vector<Command::Property> PluginUnloadCommand::properties() const +{ + return {{ "plugin", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json PluginUnloadCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({{ "plugin", args.arg(0) }}); +} + +nlohmann::json PluginUnloadCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.plugins().unload(request["plugin"].get<std::string>()); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-plugin-unload.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-plugin-unload.hpp -- implementation of plugin-unload transport command + * + * Copyright (c) 2013-2016 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_CMD_PLUGIN_UNLOAD_HPP +#define IRCCD_CMD_PLUGIN_UNLOAD_HPP + +/** + * \file cmd-plugin-unload.hpp + * \brief Implementation of plugin-unload transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of plugin-unload transport command. + */ +class PluginUnloadCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT PluginUnloadCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_PLUGIN_UNLOAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-cmode.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,66 @@ +/* + * cmd-server-cmode.cpp -- implementation of server-cmode transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-cmode.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerChannelModeCommand::ServerChannelModeCommand() + : Command("server-cmode", "Server", "Change a channel mode") +{ +} + +std::vector<Command::Arg> ServerChannelModeCommand::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "mode", true } + }; +} + +std::vector<Command::Property> ServerChannelModeCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }}, + { "mode", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerChannelModeCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"].get<std::string>())->cmode( + request["channel"].get<std::string>(), + request["mode"].get<std::string>() + ); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-cmode.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,63 @@ +/* + * cmd-server-cmode.hpp -- implementation of server-cmode transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_CMODE_HPP +#define IRCCD_CMD_SERVER_CMODE_HPP + +/** + * \file cmd-server-cmode.hpp + * \brief Implementation of server-cmode transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-cmode transport command. + */ +class ServerChannelModeCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerChannelModeCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_CMODE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-cnotice.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,65 @@ +/* + * cmd-server-cnotice.cpp -- implementation of server-cnotice transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-cnotice.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerChannelNoticeCommand::ServerChannelNoticeCommand() + : Command("server-cnotice", "Server", "Send a channel notice") +{ +} + +std::vector<Command::Arg> ServerChannelNoticeCommand::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "message", true } + }; +} + +std::vector<Command::Property> ServerChannelNoticeCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }}, + { "message", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerChannelNoticeCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"].get<std::string>())->cnotice( + request["channel"].get<std::string>(), + request["message"].get<std::string>() + ); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-cnotice.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,72 @@ +/* + * cmd-server-cnotice.hpp -- implementation of server-cnotice transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_CNOTICE_HPP +#define IRCCD_CMD_SERVER_CNOTICE_HPP + +/** + * \file cmd-server-cnotice.hpp + * \brief Implementation of server-cnotice transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-cnotice transport command. + * + * Send a channel notice to the specified channel. + * + * { + * "command": "server-cnotice", + * "server": "the server name", + * "channel": "name", + * "message": "the message" + * } + */ +class ServerChannelNoticeCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerChannelNoticeCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_CNOTICE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-connect.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,85 @@ +/* + * cmd-server-connect.cpp -- implementation of server-connect transport command + * + * Copyright (c) 2013-2016 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 <limits> + +#include <format.h> + +#include "cmd-server-connect.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" +#include "util.hpp" + +using namespace fmt::literals; + +using json = nlohmann::json; + +namespace irccd { + +namespace command { + +ServerConnectCommand::ServerConnectCommand() + : Command("server-connect", "Server", "Connect to a server") +{ +} + +std::vector<Command::Option> ServerConnectCommand::options() const +{ + return { + { "command", "c", "command", "char", "command character to use" }, + { "nickname", "n", "nickname", "nickname", "nickname to use" }, + { "realname", "r", "realname", "realname", "realname to use" }, + { "sslverify", "S", "ssl-verify", "", "verify SSL" }, + { "ssl", "s", "ssl", "", "connect with SSL" }, + { "username", "u", "username", "", "username to use" } + }; +} + +std::vector<Command::Arg> ServerConnectCommand::args() const +{ + return { + { "id", true }, + { "host", true }, + { "port", false } + }; +} + +std::vector<Command::Property> ServerConnectCommand::properties() const +{ + return { + { "name", { json::value_t::string }}, + { "host", { json::value_t::string }} + }; +} + +json ServerConnectCommand::exec(Irccd &irccd, const json &request) const +{ + auto server = Server::fromJson(request); + + if (irccd.servers().has(server->name())) + throw std::invalid_argument("server '{}' already exists"_format(server->name())); + + irccd.servers().add(std::move(server)); + + return Command::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-connect.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-connect.hpp -- implementation of server-connect transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_CONNECT_HPP +#define IRCCD_CMD_SERVER_CONNECT_HPP + +/** + * \file cmd-server-connect.hpp + * \brief Implementation of server-connect transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-connect transport command. + */ +class ServerConnectCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerConnectCommand(); + + /** + * \copydoc Command::options + */ + IRCCD_EXPORT std::vector<Option> options() const override; + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_CONNECT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-disconnect.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,52 @@ +/* + * cmd-server-disconnect.cpp -- implementation of server-disconnect transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-disconnect.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerDisconnectCommand::ServerDisconnectCommand() + : Command("server-disconnect", "Server", "Disconnect one or more servers") +{ +} + +std::vector<Command::Arg> ServerDisconnectCommand::args() const +{ + return {{ "server", false }}; +} + +nlohmann::json ServerDisconnectCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + auto it = request.find("server"); + + if (it == request.end()) + irccd.servers().clear(); + else + irccd.servers().remove(*it); + + return Command::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-disconnect.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,60 @@ +/* + * cmd-server-disconnect.hpp -- implementation of server-disconnect transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_DISCONNECT_HPP +#define IRCCD_CMD_SERVER_DISCONNECT_HPP + +/** + * \file cmd-server-disconnect.hpp + * \brief Implementation of server-disconnect transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-disconnect transport command. + */ +class ServerDisconnectCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerDisconnectCommand(); + + /** + * Get list of arguments required. + * + * \return the arguments required + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_DISCONNECT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-info.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,114 @@ +/* + * cmd-server-info.cpp -- implementation of server-info transport command + * + * Copyright (c) 2013-2016 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 <iostream> + +#include "cmd-server-info.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerInfoCommand::ServerInfoCommand() + : Command("server-info", "Server", "Get server information") +{ +} + +std::vector<Command::Arg> ServerInfoCommand::args() const +{ + return {{ "server", true }}; +} + +std::vector<Command::Property> ServerInfoCommand::properties() const +{ + return {{ "server", { nlohmann::json::value_t::string }}}; +} + +nlohmann::json ServerInfoCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return {{ "server", args.args()[0] }}; +} + +nlohmann::json ServerInfoCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + auto response = Command::exec(irccd, request); + auto server = irccd.servers().require(request["server"]); + + // General stuff. + response.push_back({"name", server->name()}); + response.push_back({"host", server->host()}); + response.push_back({"port", server->port()}); + response.push_back({"nickname", server->nickname()}); + response.push_back({"username", server->username()}); + response.push_back({"realname", server->realname()}); + response.push_back({"channels", server->channels()}); + + // Optional stuff. + if (server->flags() & Server::Ipv6) + response.push_back({"ipv6", true}); + if (server->flags() & Server::Ssl) + response.push_back({"ssl", true}); + if (server->flags() & Server::SslVerify) + response.push_back({"sslVerify", true}); + + return response; +} + +void ServerInfoCommand::result(Irccdctl &irccdctl, const nlohmann::json &response) const +{ + Command::result(irccdctl, response); + + auto get = [&] (auto key) -> std::string { + auto v = response.find(key); + + if (v == response.end() || !v->is_primitive()) + return ""; + + return v->dump(); + }; + + // Server information. + std::cout << std::boolalpha; + std::cout << "Name : " << get("name") << std::endl; + std::cout << "Host : " << get("host") << std::endl; + std::cout << "Port : " << get("port") << std::endl; + std::cout << "Ipv6 : " << get("ipv6") << std::endl; + std::cout << "SSL : " << get("ssl") << std::endl; + std::cout << "SSL verified : " << get("sslVerify") << std::endl; + + // Channels. + std::cout << "Channels : "; + + if (response.count("channels") != 0) + for (const auto &v : response["channels"]) + std::cout << v.dump() << " "; + + std::cout << std::endl; + + // Identity. + std::cout << "Nickname : " << get("nickname") << std::endl; + std::cout << "User name : " << get("username") << std::endl; + std::cout << "Real name : " << get("realname") << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-info.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,73 @@ +/* + * cmd-server-info.hpp -- implementation of server-info transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_INFO_HPP +#define IRCCD_CMD_SERVER_INFO_HPP + +/** + * \file cmd-server-info.hpp + * \brief Implementation of server-info transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-info transport command. + */ +class ServerInfoCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerInfoCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; + + /** + * \copydoc Command::result + */ + IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_INFO_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-invite.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,72 @@ +/* + * cmd-server-invite.cpp -- implementation of server-invite transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-invite.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerInviteCommand::ServerInviteCommand() + : Command("server-invite", "Server", "Invite someone into a channel") +{ +} + +std::vector<Command::Arg> ServerInviteCommand::args() const +{ + return { + { "server", true }, + { "nickname", true }, + { "channel", true } + }; +} + +std::vector<Command::Property> ServerInviteCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "target", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerInviteCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.args()[0] }, + { "target", args.args()[1] }, + { "channel", args.args()[2] } + }); +} + +nlohmann::json ServerInviteCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->invite(request["target"], request["channel"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-invite.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-invite.hpp -- implementation of server-invite transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_INVITE_HPP +#define IRCCD_CMD_SERVER_INVITE_HPP + +/** + * \file cmd-server-invite.hpp + * \brief Implementation of server-invite transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-invite transport command. + */ +class ServerInviteCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerInviteCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_INVITE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-join.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,83 @@ +/* + * cmd-server-join.cpp -- implementation of server-join transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-join.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerJoinCommand::ServerJoinCommand() + : Command("server-join", "Server", "Join a channel") +{ +} + +std::vector<Command::Arg> ServerJoinCommand::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "password", false } + }; +} + +std::vector<Command::Property> ServerJoinCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerJoinCommand::request(Irccdctl &, const CommandRequest &args) const +{ + auto req = nlohmann::json::object({ + { "server", args.args()[0] }, + { "channel", args.args()[1] } + }); + + if (args.length() == 3) + req.push_back({"password", args.args()[2]}); + + return req; +} + +nlohmann::json ServerJoinCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + std::string password; + + if (request.find("password") != request.end()) + password = request["password"]; + + irccd.servers().require( + request.at("server").get<std::string>())->join( + request.at("channel").get<std::string>(), + password + ); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-join.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-join.hpp -- implementation of server-join transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_JOIN_HPP +#define IRCCD_CMD_SERVER_JOIN_HPP + +/** + * \file cmd-server-join.hpp + * \brief Implementation of server-join transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-join transport command. + */ +class ServerJoinCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerJoinCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_JOIN_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-kick.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,81 @@ +/* + * cmd-server-kick.cpp -- implementation of server-kick transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-kick.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerKickCommand::ServerKickCommand() + : Command("server-kick", "Server", "Kick someone from a channel") +{ +} + +std::vector<Command::Arg> ServerKickCommand::args() const +{ + return { + { "server", true }, + { "target", true }, + { "channel", true }, + { "reason", false } + }; +} + +std::vector<Command::Property> ServerKickCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "target", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerKickCommand::request(Irccdctl &, const CommandRequest &args) const +{ + auto req = nlohmann::json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "channel", args.arg(2) } + }); + + if (args.length() == 4) + req.push_back({"reason", args.arg(3)}); + + return req; +} + +nlohmann::json ServerKickCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->kick( + request["target"], + request["channel"], + request.count("reason") > 0 ? request["reason"] : "" + ); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-kick.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-kick.hpp -- implementation of server-kick transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_KICK_HPP +#define IRCCD_CMD_SERVER_KICK_HPP + +/** + * \file cmd-server-kick.hpp + * \brief Implementation of server-kick transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-kick transport command. + */ +class ServerKickCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerKickCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_KICK_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-list.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,62 @@ +/* + * cmd-server-list.cpp -- implementation of server-list transport command + * + * Copyright (c) 2013-2016 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 <iostream> + +#include "cmd-server-list.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerListCommand::ServerListCommand() + : Command("server-list", "Server", "Get the list of servers") +{ +} + +nlohmann::json ServerListCommand::exec(Irccd &irccd, const nlohmann::json &) const +{ + auto json = nlohmann::json::object(); + auto list = nlohmann::json::array(); + + for (const auto &server : irccd.servers().servers()) + list.push_back(server->name()); + + json.push_back({"list", std::move(list)}); + + return json; +} + +void ServerListCommand::result(Irccdctl &, const nlohmann::json &response) const +{ + auto list = response.find("list"); + + if (list == response.end()) + return; + + for (auto v : *list) + if (v.is_string()) + std::cout << v.get<std::string>() << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-list.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,58 @@ +/* + * cmd-server-list.hpp -- implementation of server-list transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_LIST_HPP +#define IRCCD_CMD_SERVER_LIST_HPP + +/** + * \file cmd-server-list.hpp + * \brief Implementation of server-list transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-list transport command. + */ +class ServerListCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerListCommand(); + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; + + /** + * \copydoc Command::result + */ + IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_LIST_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-me.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,71 @@ +/* + * cmd-server-me.cpp -- implementation of server-me transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-me.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerMeCommand::ServerMeCommand() + : Command("server-me", "Server", "Send an action emote") +{ +} + +std::vector<Command::Arg> ServerMeCommand::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +std::vector<Command::Property> ServerMeCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "target", { nlohmann::json::value_t::string }}, + { "message", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerMeCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +nlohmann::json ServerMeCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->me(request["target"], request["message"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-me.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-me.hpp -- implementation of server-me transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_ME_HPP +#define IRCCD_CMD_SERVER_ME_HPP + +/** + * \file cmd-server-me.hpp + * \brief Implementation of server-me transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-me transport command. + */ +class ServerMeCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerMeCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_ME_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-message.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,71 @@ +/* + * cmd-server-message.cpp -- implementation of server-message transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-message.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerMessageCommand::ServerMessageCommand() + : Command("server-message", "Server", "Send a message") +{ +} + +std::vector<Command::Arg> ServerMessageCommand::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +std::vector<Command::Property> ServerMessageCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "target", { nlohmann::json::value_t::string }}, + { "message", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerMessageCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +nlohmann::json ServerMessageCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->me(request["target"], request["message"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-message.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-message.hpp -- implementation of server-message transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_MESSAGE_HPP +#define IRCCD_CMD_SERVER_MESSAGE_HPP + +/** + * \file cmd-server-message.hpp + * \brief Implementation of server-message transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-message transport command. + */ +class ServerMessageCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerMessageCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_MESSAGE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-mode.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-mode.cpp -- implementation of server-mode transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-mode.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerModeCommand::ServerModeCommand() + : Command("server-mode", "Server", "Change your mode") +{ +} + +std::vector<Command::Arg> ServerModeCommand::args() const +{ + return { + { "server", true }, + { "mode", true } + }; +} + +std::vector<Command::Property> ServerModeCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "mode", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerModeCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "mode", args.arg(1) } + }); +} + +nlohmann::json ServerModeCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->mode(request["mode"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-mode.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-mode.hpp -- implementation of server-mode transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_MODE_HPP +#define IRCCD_CMD_SERVER_MODE_HPP + +/** + * \file cmd-server-mode.hpp + * \brief Implementation of server-mode transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-mode transport command. + */ +class ServerModeCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerModeCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_MODE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-nick.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-nick.cpp -- implementation of server-nick transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-nick.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerNickCommand::ServerNickCommand() + : Command("server-nick", "Server", "Change your nickname") +{ +} + +std::vector<Command::Arg> ServerNickCommand::args() const +{ + return { + { "server", true }, + { "nickname", true } + }; +} + +std::vector<Command::Property> ServerNickCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "nickname", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerNickCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "nickname", args.arg(1) } + }); +} + +nlohmann::json ServerNickCommand::exec(Irccd &irccd, const nlohmann::json &object) const +{ + Command::exec(irccd, object); + + irccd.servers().require(object["server"])->setNickname(object["nickname"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-nick.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-nick.hpp -- implementation of server-nick transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_NICK_HPP +#define IRCCD_CMD_SERVER_NICK_HPP + +/** + * \file cmd-server-nick.hpp + * \brief Implementation of server-nick transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-nick transport command. + */ +class ServerNickCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerNickCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_NICK_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-notice.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,71 @@ +/* + * cmd-server-notice.cpp -- implementation of server-notice transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-notice.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerNoticeCommand::ServerNoticeCommand() + : Command("server-notice", "Server", "Send a private notice") +{ +} + +std::vector<Command::Arg> ServerNoticeCommand::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +std::vector<Command::Property> ServerNoticeCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "target", { nlohmann::json::value_t::string }}, + { "message", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerNoticeCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +nlohmann::json ServerNoticeCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->notice(request["target"], request["message"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-notice.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-notice.hpp -- implementation of server-notice transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_NOTICE_HPP +#define IRCCD_CMD_SERVER_NOTICE_HPP + +/** + * \file cmd-server-notice.hpp + * \brief Implementation of server-notice transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-notice transport command. + */ +class ServerNoticeCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerNoticeCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_NOTICE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-part.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,77 @@ +/* + * cmd-server-part.cpp -- implementation of server-part transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-part.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerPartCommand::ServerPartCommand() + : Command("server-part", "Server", "Leave a channel") +{ +} + +std::vector<Command::Arg> ServerPartCommand::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "reason", false } + }; +} + +std::vector<Command::Property> ServerPartCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerPartCommand::request(Irccdctl &, const CommandRequest &args) const +{ + auto req = nlohmann::json::object({ + { "server", args.arg(0) }, + { "channel", args.arg(1) } + }); + + if (args.length() == 3) + req.push_back({"reason", args.arg(2)}); + + return req; +} + +nlohmann::json ServerPartCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->part( + request["channel"], + request.count("reason") > 0 ? request["reason"] : "" + ); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-part.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,69 @@ +/* + * cmd-server-part.hpp -- implementation of server-part transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_PART_HPP +#define IRCCD_CMD_SERVER_PART_HPP + +/** + * \file cmd-server-part.hpp + * \brief Implementation of server-part transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \class ServerPart + * \brief Implementation of server-part transport command. + */ +class ServerPartCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerPartCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_PART_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-reconnect.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,58 @@ +/* + * cmd-server-reconnect.cpp -- implementation of server-reconnect transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-reconnect.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerReconnectCommand::ServerReconnectCommand() + : Command("server-reconnect", "Server", "Force reconnection of one or more servers") +{ +} + +std::vector<Command::Arg> ServerReconnectCommand::args() const +{ + return {{ "server", false }}; +} + +nlohmann::json ServerReconnectCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return args.length() == 0 ? nlohmann::json::object() : nlohmann::json::object({ { "server", args.arg(0) } }); +} + +nlohmann::json ServerReconnectCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + auto server = request.find("server"); + + if (server != request.end() && server->is_string()) + irccd.servers().require(*server)->reconnect(); + else + for (auto &server : irccd.servers().servers()) + server->reconnect(); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-reconnect.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,63 @@ +/* + * cmd-server-reconnect.hpp -- implementation of server-reconnect transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_RECONNECT_HPP +#define IRCCD_CMD_SERVER_RECONNECT_HPP + +/** + * \file cmd-server-reconnect.hpp + * \brief Implementation of server-reconnect transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-reconnect transport command. + */ +class ServerReconnectCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerReconnectCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_RECONNECT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-topic.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,71 @@ +/* + * cmd-server-topic.cpp -- implementation of server-topic transport command + * + * Copyright (c) 2013-2016 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 "cmd-server-topic.hpp" +#include "irccd.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace command { + +ServerTopicCommand::ServerTopicCommand() + : Command("server-topic", "Server", "Change a channel topic") +{ +} + +std::vector<Command::Arg> ServerTopicCommand::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "topic", true } + }; +} + +std::vector<Command::Property> ServerTopicCommand::properties() const +{ + return { + { "server", { nlohmann::json::value_t::string }}, + { "channel", { nlohmann::json::value_t::string }}, + { "topic", { nlohmann::json::value_t::string }} + }; +} + +nlohmann::json ServerTopicCommand::request(Irccdctl &, const CommandRequest &args) const +{ + return nlohmann::json::object({ + { "server", args.arg(0) }, + { "channel", args.arg(1) }, + { "topic", args.arg(2) } + }); +} + +nlohmann::json ServerTopicCommand::exec(Irccd &irccd, const nlohmann::json &request) const +{ + Command::exec(irccd, request); + + irccd.servers().require(request["server"])->topic(request["channel"], request["topic"]); + + return nlohmann::json::object(); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/cmd-server-topic.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,68 @@ +/* + * cmd-server-topic.hpp -- implementation of server-topic transport command + * + * Copyright (c) 2013-2016 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_CMD_SERVER_TOPIC_HPP +#define IRCCD_CMD_SERVER_TOPIC_HPP + +/** + * \file cmd-server-topic.hpp + * \brief Implementation of server-topic transport command. + */ + +#include "command.hpp" + +namespace irccd { + +namespace command { + +/** + * \brief Implementation of server-topic transport command. + */ +class ServerTopicCommand : public Command { +public: + /** + * Constructor. + */ + IRCCD_EXPORT ServerTopicCommand(); + + /** + * \copydoc Command::args + */ + IRCCD_EXPORT std::vector<Arg> args() const override; + + /** + * \copydoc Command::properties + */ + IRCCD_EXPORT std::vector<Property> properties() const override; + + /** + * \copydoc Command::request + */ + IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override; + + /** + * \copydoc Command::exec + */ + IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !IRCCD_CMD_SERVER_TOPIC_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/command.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,272 @@ +/* + * command.cpp -- remote command + * + * Copyright (c) 2013-2016 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 <iomanip> +#include <numeric> +#include <sstream> + +#include <format.h> + +#include "command.hpp" +#include "logger.hpp" +#include "system.hpp" + +using namespace std::string_literals; + +using namespace fmt::literals; + +using json = nlohmann::json; + +namespace irccd { + +namespace { + +/* + * typeName + * ------------------------------------------------------------------ + * + * Convert a JSON value type to string for convenience. + */ +std::string typeName(nlohmann::json::value_t type) noexcept +{ + switch (type) { + case nlohmann::json::value_t::array: + return "array"; + case nlohmann::json::value_t::boolean: + return "bool"; + case nlohmann::json::value_t::number_float: + return "float"; + case nlohmann::json::value_t::number_integer: + return "integer"; + case nlohmann::json::value_t::number_unsigned: + return "unsigned"; + case nlohmann::json::value_t::null: + return "null"; + case nlohmann::json::value_t::object: + return "object"; + case nlohmann::json::value_t::string: + return "string"; + default: + return ""; + } +} + +/* + * typeNameList + * ------------------------------------------------------------------ + * + * Construct a list of names to send a convenient error message if properties + * are invalid, example: string, int or bool expected. + */ + +std::string typeNameList(const std::vector<json::value_t> &types) +{ + std::ostringstream oss; + + if (types.size() == 1) + return typeName(types[0]); + + for (std::size_t i = 0; i < types.size(); ++i) { + oss << typeName(types[i]); + + if (i == types.size() - 2) + oss << " or "; + else if (i < types.size() - 1) + oss << ", "; + } + + return oss.str(); +} + +} // !namespace + +/* + * JSON errors + * ------------------------------------------------------------------ + */ + +MissingPropertyError::MissingPropertyError(std::string name, std::vector<nlohmann::json::value_t> types) + : m_name(std::move(name)) + , m_types(std::move(types)) +{ + m_message = "missing '" + m_name + "' property (" + typeNameList(m_types) + " expected)"; +} + +InvalidPropertyError::InvalidPropertyError(std::string name, nlohmann::json::value_t expected, nlohmann::json::value_t result) + : m_name(std::move(name)) + , m_expected(expected) + , m_result(result) +{ + m_message += "invalid '" + m_name + "' property "; + m_message += "(" + typeName(expected) + " expected, "; + m_message += "got " + typeName(result) + ")"; +} + +PropertyRangeError::PropertyRangeError(std::string name, std::uint64_t min, std::uint64_t max, std::uint64_t value) + : m_name(std::move(name)) + , m_min(min) + , m_max(max) + , m_value(value) +{ + assert(value < min || value > max); + + m_message += "property '" + m_name + "' is out of range "; + m_message += std::to_string(min) + ".." + std::to_string(max) + ", got " + std::to_string(value); +} + +PropertyError::PropertyError(std::string name, std::string message) + : m_name(std::move(name)) +{ + m_message += "property '" + m_name + "': " + message; +} + +/* + * Command implementation + * ------------------------------------------------------------------ + */ + +std::string Command::usage() const +{ + std::ostringstream oss; + + oss << m_name << " "; + + // Options. + auto optlist = options(); + + if (optlist.size() > 0) { + for (const auto &opt : optlist) { + oss << "["; + + /* + * Long options are too big so only show them in the help + * command usage or only if no short option is available. + */ + if (opt.simpleKey().size() > 0) + oss << "-" << opt.simpleKey(); + else if (opt.longKey().size() > 0) + oss << " --" << opt.longKey(); + + oss << (opt.arg().empty() ? "" : " ") << opt.arg() << "] "; + } + } + + // Arguments. + auto argslist = args(); + + if (argslist.size() > 0) { + for (const auto &arg : argslist) + oss << (arg.required() ? "" : "[") + << arg.name() + << (arg.required() ? "" : "]") << " "; + } + + return oss.str(); +} + +std::string Command::help() const +{ + std::ostringstream oss; + + oss << "usage: " << sys::programName() << " " << m_name; + + // Options summary. + if (options().size() > 0) + oss << " [options...]"; + + // Arguments summary. + if (args().size() > 0) { + oss << " "; + + for (const auto &arg : args()) + oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " "; + } + + // Description. + oss << "\n\n" << m_description << "\n\n"; + + // Options. + if (options().size() > 0) { + oss << "Options:\n"; + + for (const auto &opt : options()) { + std::ostringstream optoss; + + // Construct the line for the option in a single string to pad it correctly. + optoss << " "; + optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : " "); + optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : ""); + optoss << opt.arg(); + + // Add it padded with spaces. + oss << std::left << std::setw(28) << optoss.str(); + oss << opt.description() << "\n"; + } + } + + return oss.str(); +} + +unsigned Command::min() const noexcept +{ + auto list = args(); + + return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned { + return i + (arg.required() ? 1 : 0); + }); +} + +unsigned Command::max() const noexcept +{ + return (unsigned)args().size(); +} + +nlohmann::json Command::request(Irccdctl &, const CommandRequest &) const +{ + return nlohmann::json::object({}); +} + +nlohmann::json Command::exec(Irccd &, const nlohmann::json &request) const +{ + // Verify that requested properties are present in the request. + for (const auto &prop : properties()) { + auto it = request.find(prop.name()); + + if (it == request.end()) + throw std::invalid_argument("missing '{}' property"_format(prop.name())); + + if (std::find(prop.types().begin(), prop.types().end(), it->type()) == prop.types().end()) { + auto expected = typeNameList(prop.types()); + auto got = typeName(it->type()); + + throw std::invalid_argument("invalid '{}' property ({} expected, got {})"_format(prop.name(), expected, got)); + } + } + + return nlohmann::json::object({}); +} + +void Command::result(Irccdctl &, const nlohmann::json &response) const +{ + auto it = response.find("error"); + + if (it != response.end() && it->is_string()) + log::warning() << "irccdctl: " << it->dump() << std::endl; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/command.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,709 @@ +/* + * command.hpp -- remote command + * + * Copyright (c) 2013-2016 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_COMMAND_HPP +#define IRCCD_COMMAND_HPP + +/** + * \file command.hpp + * \brief Remote commands. + */ + +#include <cassert> +#include <map> +#include <vector> + +#include "json.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +class Irccd; +class Irccdctl; + +/** + * \brief A JSON property is missing. + */ +class MissingPropertyError : public std::exception { +private: + std::string m_message; + std::string m_name; + std::vector<nlohmann::json::value_t> m_types; + +public: + /** + * Constructor. + */ + MissingPropertyError(std::string name, std::vector<nlohmann::json::value_t> types); + + /** + * Get human error message. + * + * \return a message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + +/** + * \brief A JSON property is invalid + */ +class InvalidPropertyError : public std::exception { +private: + std::string m_message; + std::string m_name; + + nlohmann::json::value_t m_expected; + nlohmann::json::value_t m_result; + +public: + /** + * Constructor. + * + * \param name the property name + * \param expected the expected type + * \param result the type received + */ + InvalidPropertyError(std::string name, nlohmann::json::value_t expected, nlohmann::json::value_t result); + + /** + * Get human error message. + * + * \return a message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + +/** + * \brief Property range error. + */ +class PropertyRangeError : public std::exception { +private: + std::string m_message; + std::string m_name; + std::uint64_t m_min; + std::uint64_t m_max; + std::uint64_t m_value; + +public: + /** + * Constructor. + * + * \pre value < min || value > max + * \param name the property name + * \param min the minimum value + * \param max the maximum value + * \param value the actual value + */ + PropertyRangeError(std::string name, std::uint64_t min, std::uint64_t max, std::uint64_t value); + + /** + * Get human error message. + * + * \return a message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + +/** + * \brief Generic error for JSON properties. + */ +class PropertyError : public std::exception { +private: + std::string m_message; + std::string m_name; + +public: + /** + * Constructor. + * + * \param name the property name + * \param message the error message + */ + PropertyError(std::string name, std::string message); + + /** + * Get human error message. + * + * \return a message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + + +/** + * \brief Namespace for remote commands. + */ +//namespace command { + +/** + * \brief Command line arguments to irccdctl. + * + * This class contains the resolved arguments from command line that can apply + * to the command. + */ +class CommandRequest { +public: + /** + * The options given by command line. + */ + using Options = std::multimap<std::string, std::string>; + + /** + * Command line arguments in the same order. + */ + using Args = std::vector<std::string>; + +private: + Options m_options; + Args m_args; + +public: + /** + * Construct the request. + * + * \param options the options + * \param args the arguments + */ + inline CommandRequest(Options options, Args args) noexcept + : m_options(std::move(options)) + , m_args(std::move(args)) + { + } + + /** + * Get the arguments. + * + * \return the arguments + */ + inline const Args &args() const noexcept + { + return m_args; + } + + /** + * Get the options. + * + * \return the options + */ + inline const Options &options() const noexcept + { + return m_options; + } + + /** + * Get the number of arguments. + * + * \return the number of arguments + */ + inline unsigned length() const noexcept + { + return (unsigned)m_args.size(); + } + + /** + * Check if the request has the given option id. + * + * \param option the option id + * \return true if the option is available + */ + inline bool has(const std::string &option) const noexcept + { + return m_options.count(option) != 0; + } + + /** + * Get the argument at the specified index. + * + * \pre index < length() + * \param index the argument index + * \return the argument + */ + inline const std::string &arg(unsigned index) const noexcept + { + assert(index < m_args.size()); + + return m_args[index]; + } + + /** + * Get the argument or default value if not available. + * + * \param index the index + * \param defaultValue the value if index is out of range + * \return the argument + */ + inline std::string argOr(unsigned index, std::string defaultValue) const noexcept + { + return index < m_args.size() ? m_args[index] : defaultValue; + } + + /** + * Get the given option by its id. + * + * \pre has(key) + * \param key the option id + * \return the option + */ + inline const std::string &option(const std::string &key) const noexcept + { + assert(m_options.count(key) != 0); + + return m_options.find(key)->second; + } + + /** + * Get the given option by its id or defaultValue if not found. + * + * \param key the option id + * \param defaultValue the value replacement + * \return the option + */ + inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept + { + auto it = m_options.find(key); + + if (it == m_options.end()) + return defaultValue; + + return it->second; + } +}; + +/** + * \brief Invokable command. + * + * A remote command is a invokable command in the irccd daemon. You can register + * dynamically any remote command you like using Application::addCommand. + * + * The remote command will be usable directly from irccdctl without any other + * code. + * + * A remote command can have options and arguments. Options always come first, + * before arguments. + * + * The command workflow is defined as follow: + * + * 1. User wants to invoke a command, request() is called and return a JSON + * object containaing the request, it it send to the daemon. + * + * 2. The daemon receive the request and execute it using exec(). It returns a + * JSON object containint the request result or error if any. + * + * 3. Finally, the command receives the result in result() function and user can + * manipulate it. For convenience, the default implementation shows the error + * if any. + */ +class Command { +public: + /** + * \brief Defines available options for this command. + */ + class Option; + + /** + * \brief Defines available arguments for this command. + */ + class Arg; + + /** + * \brief Defines properties that must be available in the JSON request. + */ + class Property; + +private: + std::string m_name; + std::string m_category; + std::string m_description; + bool m_visible; + +public: + /** + * Create the remote command. + * + * \pre name must not be empty + * \pre category must not be empty + * \param name the command name (e.g. server-list) + * \param category the category (e.g. Server) + * \param description a one line description with no dots, no new line + * \param visible true if the command should be visible without verbosity + */ + inline Command(std::string name, + std::string category, + std::string description, + bool visible = true) noexcept + : m_name(std::move(name)) + , m_category(std::move(category)) + , m_description(std::move(description)) + , m_visible(visible) + { + assert(!m_name.empty()); + assert(!m_category.empty()); + } + + /** + * Default destructor virtual. + */ + virtual ~Command() = default; + + /** + * Return the command name, must not have spaces. + * + * \return the command name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Get the command category. + * + * Irccdctl will sort commands by categories. + * + * \return the category + */ + inline const std::string &category() const noexcept + { + return m_category; + } + + /** + * Get the command description. + * + * \return the description + */ + inline const std::string &description() const noexcept + { + return m_description; + } + + /** + * Hide the command in non-verbose mode. + * + * \return true if the command should be visible in non-verbose mode + */ + inline bool visible() const noexcept + { + return m_visible; + } + + /** + * Return the command documentation usage. + * + * \return the usage + */ + IRCCD_EXPORT std::string usage() const; + + /** + * Return the help message. + * + * \return the help message + */ + IRCCD_EXPORT std::string help() const; + + /** + * Get the supported irccdctl options. + * + * \return the options + */ + virtual std::vector<Option> options() const + { + return {}; + } + + /** + * Get the supported arguments. + * + * \return the arguments + */ + virtual std::vector<Arg> args() const + { + return {}; + } + + /** + * Get the properties required in the JSON request. + * + * Default implementation returns empty list. + * + * \return the required properties + * \note Put only **required** properties + */ + virtual std::vector<Property> properties() const + { + return {}; + } + + /** + * Get the minimum number of arguments required. + * + * \return the minimum + */ + IRCCD_EXPORT unsigned min() const noexcept; + + /** + * Get the maximum number of arguments required. + * + * \return the maximum + */ + IRCCD_EXPORT unsigned max() const noexcept; + + /** + * Prepare a JSON request to the daemon. + * + * If the command is local and does not need to send anything to irccd's + * instance, return a null JSON value. + * + * The default implementation just send the command name with no arguments. + * + * \param irccdctl the irccdctl instance + * \param args the command line arguments and options + * \return the JSON object to send to the daemon + * \post the returned JSON value must be an object + */ + IRCCD_EXPORT virtual nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const; + + /** + * Execute the command in the daemon. + * + * The user can return an object with any properties to forward to the + * client. Irccd will automatically add the command name and the appropriate + * status code. + * + * The default return an empty object which indicates success. + * + * If any exception is thrown from this function, it is forwarded to the + * client as error status. + * + * \param irccd the instance + * \param request the JSON request + * \return the response + */ + IRCCD_EXPORT virtual nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const; + + /** + * What to do when receiving the response from irccd. + * + * This default implementation just check for an error string and shows it + * if any. + * + * \param irccdctl the irccdctl instance + * \param response the JSON response + */ + IRCCD_EXPORT virtual void result(Irccdctl &irccdctl, const nlohmann::json &response) const; +}; + +/** + * \brief Option description for a command. + */ +class Command::Option { +private: + std::string m_id; + std::string m_simple; + std::string m_long; + std::string m_arg; + std::string m_description; + +public: + /** + * Constructor an option description. + * + * Simple and long keys must not start with '-' or '--', they will be added + * automatically. + * + * If arg is not empty, the option takes an argument. + * + * \pre id must not be empty + * \pre at least simpleKey or longKey must not be empty + * \pre description must not be empty + * \param id the option id + * \param simpleKey the key the option key + * \param longKey the long option name + * \param arg the argument name if needed + * \param description the description + */ + inline Option(std::string id, + std::string simpleKey, + std::string longKey, + std::string arg, + std::string description) noexcept + : m_id(std::move(id)) + , m_simple(std::move(simpleKey)) + , m_long(std::move(longKey)) + , m_arg(std::move(arg)) + , m_description(std::move(description)) + { + assert(!m_id.empty()); + assert(!m_simple.empty() || !m_long.empty()); + assert(!m_description.empty()); + } + + /** + * Get the id. + * + * \return the id + */ + inline const std::string &id() const noexcept + { + return m_id; + } + + /** + * Get the option key. + * + * \return the key + */ + inline const std::string &simpleKey() const noexcept + { + return m_simple; + } + + /** + * Get the long option. + * + * \return the long option + */ + inline const std::string &longKey() const noexcept + { + return m_long; + } + + /** + * Get the option description. + * + * \return the description + */ + inline const std::string &description() const noexcept + { + return m_description; + } + + /** + * Get the option argument name. + * + * \return the argument name if any + */ + inline const std::string &arg() const noexcept + { + return m_arg; + } +}; + +/** + * \brief Argument description for command. + */ +class Command::Arg { +private: + std::string m_name; + bool m_required; + +public: + /** + * Construct an argument. + * + * \param name the name + * \param required true if the argument is required + */ + inline Arg(std::string name, bool required) noexcept + : m_name(std::move(name)) + , m_required(required) + { + } + + /** + * Get the argument name. + * + * \return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Tells if the argument is required. + * + * \return true if required + */ + inline bool required() const noexcept + { + return m_required; + } +}; + +/** + * \brief Property description for JSON request. + */ +class Command::Property { +private: + std::string m_name; + std::vector<nlohmann::json::value_t> m_types; + +public: + /** + * Construct the property description. + * + * \pre !name.empty() + * \pre types.size() >= 1 + * \param name the name + * \param types the json types allowed + */ + inline Property(std::string name, std::vector<nlohmann::json::value_t> types = { nlohmann::json::value_t::string }) noexcept + : m_name(std::move(name)) + , m_types(std::move(types)) + { + assert(!m_name.empty()); + assert(m_types.size() >= 1); + } + + /** + * Get the property name. + * + * \return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Get the property types. + * + * \return the types + */ + inline const std::vector<nlohmann::json::value_t> &types() const noexcept + { + return m_types; + } +}; + +} // !irccd + +#endif // !IRCCD_COMMAND_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/config.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,568 @@ +/* + * config.cpp -- irccd configuration loader + * + * Copyright (c) 2013-2016 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 <cassert> + +#include <format.h> + +#include "config.hpp" +#include "fs.hpp" +#include "irccd.hpp" +#include "logger.hpp" +#include "path.hpp" +#include "plugin.hpp" +#include "rule.hpp" +#include "server.hpp" +#include "service-plugin.hpp" +#include "sysconfig.hpp" +#include "transport.hpp" +#include "util.hpp" + +using namespace fmt::literals; + +namespace irccd { + +namespace { + +class IrccdLogFilter : public log::Filter { +private: + std::string convert(const std::string &tmpl, std::string input) const + { + if (tmpl.empty()) + return input; + + util::Substitution params; + + params.flags &= ~(util::Substitution::IrcAttrs); + params.keywords.emplace("message", std::move(input)); + + return util::format(tmpl, params); + } + +public: + std::string m_debug; + std::string m_info; + std::string m_warning; + + std::string preDebug(std::string input) const override + { + return convert(m_debug, std::move(input)); + } + + std::string preInfo(std::string input) const override + { + return convert(m_info, std::move(input)); + } + + std::string preWarning(std::string input) const override + { + return convert(m_warning, std::move(input)); + } +}; + +std::string get(const ini::Document &doc, const std::string §ion, const std::string &key) +{ + auto its = doc.find(section); + + if (its == doc.end()) + return ""; + + auto ito = its->find(key); + + if (ito == its->end()) + return ""; + + return ito->value(); +} + +PluginConfig loadPluginConfig(const ini::Section &sc) +{ + PluginConfig config; + + for (const auto &option : sc) + config.emplace(option.key(), option.value()); + + return config; +} + +std::unique_ptr<log::Logger> loadLogFile(const ini::Section &sc) +{ + /* + * TODO: improve that with CMake options. + */ +#if defined(IRCCD_SYSTEM_WINDOWS) + std::string normal = "log.txt"; + std::string errors = "errors.txt"; +#else + std::string normal = "/var/log/irccd/log.txt"; + std::string errors = "/var/log/irccd/errors.txt"; +#endif + + ini::Section::const_iterator it; + + if ((it = sc.find("path-logs")) != sc.end()) + normal = it->value(); + if ((it = sc.find("path-errors")) != sc.end()) + errors = it->value(); + + return std::make_unique<log::FileLogger>(std::move(normal), std::move(errors)); +} + +std::unique_ptr<log::Logger> loadLogSyslog() +{ +#if defined(HAVE_SYSLOG) + return std::make_unique<log::SyslogLogger>(); +#else + throw std::runtime_error("logs: syslog is not available on this platform"); +#endif // !HAVE_SYSLOG +} + +std::shared_ptr<TransportServer> loadTransportIp(const ini::Section &sc) +{ + assert(sc.key() == "transport"); + + std::shared_ptr<TransportServer> transport; + ini::Section::const_iterator it; + + // Port. + int port; + + if ((it = sc.find("port")) == sc.cend()) + throw std::invalid_argument("transport: missing 'port' parameter"); + + try { + port = util::toNumber<std::uint16_t>(it->value()); + } catch (const std::exception &) { + throw std::invalid_argument("transport: invalid port number: {}"_format(it->value())); + } + + // Address. + std::string address = "*"; + + if ((it = sc.find("address")) != sc.end()) + address = it->value(); + + // Domain + std::uint8_t mode = TransportServerIp::v4; + + if ((it = sc.find("domain")) != sc.end()) { + mode = 0; + + for (const auto &v : *it) { + if (v == "ipv4") + mode |= TransportServerIp::v4; + if (v == "ipv6") + mode |= TransportServerIp::v6; + } + } + + // Optional SSL. + std::string pkey; + std::string cert; + + if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value())) { + if ((it = sc.find("certificate")) == sc.end()) + throw std::invalid_argument("transport: missing 'certificate' parameter"); + + cert = it->value(); + + if ((it = sc.find("key")) == sc.end()) + throw std::invalid_argument("transport: missing 'key' parameter"); + + pkey = it->value(); + } + + if (mode == 0) + throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6"); + + if (pkey.empty()) + return std::make_shared<TransportServerIp>(address, port, mode); + + return std::make_shared<TransportServerTls>(pkey, cert, address, port, mode); +} + +std::shared_ptr<TransportServer> loadTransportUnix(const ini::Section &sc) +{ + assert(sc.key() == "transport"); + +#if !defined(IRCCD_SYSTEM_WINDOWS) + ini::Section::const_iterator it = sc.find("path"); + + if (it == sc.end()) + throw std::invalid_argument("transport: missing 'path' parameter"); + + return std::make_shared<TransportServerLocal>(it->value()); +#else + (void)sc; + + throw std::invalid_argument("transport: unix transport not supported on on this platform"); +#endif +} + +std::shared_ptr<TransportServer> loadTransport(const ini::Section &sc) +{ + assert(sc.key() == "transport"); + + std::shared_ptr<TransportServer> transport; + ini::Section::const_iterator it = sc.find("type"); + + if (it == sc.end()) + throw std::invalid_argument("transport: missing 'type' parameter"); + + if (it->value() == "ip") + transport = loadTransportIp(sc); + else if (it->value() == "unix") + transport = loadTransportUnix(sc); + else + throw std::invalid_argument("transport: invalid type given: {}"_format(it->value())); + + if ((it = sc.find("password")) != sc.end()) + transport->setPassword(it->value()); + + return transport; +} + +Rule loadRule(const ini::Section &sc) +{ + assert(sc.key() == "rule"); + + // Simple converter from std::vector to std::unordered_set. + auto toSet = [] (const std::vector<std::string> &v) -> std::unordered_set<std::string> { + return std::unordered_set<std::string>(v.begin(), v.end()); + }; + + RuleSet servers, channels, origins, plugins, events; + RuleAction action = RuleAction::Accept; + + // Get the sets. + ini::Section::const_iterator it; + + if ((it = sc.find("servers")) != sc.end()) + servers = toSet(*it); + if ((it = sc.find("channels")) != sc.end()) + channels = toSet(*it); + if ((it = sc.find("origins")) != sc.end()) + origins = toSet(*it); + if ((it = sc.find("plugins")) != sc.end()) + plugins = toSet(*it); + if ((it = sc.find("channels")) != sc.end()) + channels = toSet(*it); + + // Get the action. + if ((it = sc.find("action")) == sc.end()) + throw std::invalid_argument("rule: missing 'action'' parameter"); + + if (it->value() == "drop") + action = RuleAction::Drop; + else if (it->value() == "accept") + action = RuleAction::Accept; + else + throw std::invalid_argument("rule: invalid action given: {}"_format(it->value())); + + return Rule(std::move(servers), + std::move(channels), + std::move(origins), + std::move(plugins), + std::move(events), + action); +} + +std::shared_ptr<Server> loadServer(const ini::Section &sc, const Config &config) +{ + assert(sc.key() == "server"); + + // Name. + ini::Section::const_iterator it; + + if ((it = sc.find("name")) == sc.end()) + throw std::invalid_argument("server: missing 'name' parameter"); + else if (!util::isIdentifierValid(it->value())) + throw std::invalid_argument("server: invalid identifier: {}"_format(it->value())); + + auto server = std::make_shared<Server>(it->value()); + + // Host + if ((it = sc.find("host")) == sc.end()) + throw std::invalid_argument("server {}: missing host"_format(server->name())); + + server->setHost(it->value()); + + // Optional password + if ((it = sc.find("password")) != sc.end()) + server->setPassword(it->value()); + + // Optional flags + if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value())) + server->setFlags(server->flags() | Server::Ipv6); + if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value())) + server->setFlags(server->flags() | Server::Ssl); + if ((it = sc.find("ssl-verify")) != sc.end() && util::isBoolean(it->value())) + server->setFlags(server->flags() | Server::SslVerify); + + // Optional identity + if ((it = sc.find("identity")) != sc.end()) + config.loadServerIdentity(*server, it->value()); + + // Options + if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value())) + server->setFlags(server->flags() | Server::AutoRejoin); + if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value())) + server->setFlags(server->flags() | Server::JoinInvite); + + // Channels + if ((it = sc.find("channels")) != sc.end()) { + for (const std::string &s : *it) { + Channel channel; + + if (auto pos = s.find(":") != std::string::npos) { + channel.name = s.substr(0, pos); + channel.password = s.substr(pos + 1); + } else + channel.name = s; + + //server.channels.push_back(std::move(channel)); + //server->join() + server->join(channel.name, channel.password); + } + } + if ((it = sc.find("command-char")) != sc.end()) + server->setCommandCharacter(it->value()); + + // Reconnect and ping timeout + try { + if ((it = sc.find("port")) != sc.end()) + server->setPort(util::toNumber<std::uint16_t>(it->value())); + if ((it = sc.find("reconnect-tries")) != sc.end()) + server->setReconnectTries(util::toNumber<std::int8_t>(it->value())); + if ((it = sc.find("reconnect-timeout")) != sc.end()) + server->setReconnectDelay(util::toNumber<std::uint16_t>(it->value())); + if ((it = sc.find("ping-timeout")) != sc.end()) + server->setPingTimeout(util::toNumber<std::uint16_t>(it->value())); + } catch (const std::exception &) { + log::warning("server {}: invalid number for {}: {}"_format(server->name(), it->key(), it->value())); + } + + return server; +} + +} // !namespace + +Config Config::find() +{ + for (const auto &path : path::list(path::PathConfig)) { + std::string fullpath = path + "irccd.conf"; + + if (!fs::isReadable(fullpath)) + continue; + + try { + return Config(fullpath); + } catch (const std::exception &ex) { + throw std::runtime_error("{}: {}"_format(fullpath, ex.what())); + } + } + + throw std::runtime_error("no configuration file found"); +} + +void Config::loadServerIdentity(Server &server, const std::string &identity) const +{ + ini::Document::const_iterator sc = std::find_if(m_document.begin(), m_document.end(), [&] (const auto &sc) { + if (sc.key() != "identity") + return false; + + auto name = sc.find("name"); + + return name != sc.end() && name->value() == identity; + }); + + if (sc == m_document.end()) + return; + + ini::Section::const_iterator it; + + if ((it = sc->find("username")) != sc->end()) + server.setUsername(it->value()); + if ((it = sc->find("realname")) != sc->end()) + server.setRealname(it->value()); + if ((it = sc->find("nickname")) != sc->end()) + server.setNickname(it->value()); + if ((it = sc->find("ctcp-version")) != sc->end()) + server.setCtcpVersion(it->value()); +} + +PluginConfig Config::findPluginConfig(const std::string &name) const +{ + assert(util::isIdentifierValid(name)); + + std::string fullname = std::string("plugin.") + name; + + for (const auto §ion : m_document) { + if (section.key() != fullname) + continue; + + return loadPluginConfig(section); + } + + return PluginConfig(); +} + +PluginFormats Config::findPluginFormats(const std::string &name) const +{ + assert(util::isIdentifierValid(name)); + + auto section = m_document.find(std::string("format.") + name); + + if (section == m_document.end()) + return PluginFormats(); + + PluginFormats formats; + + for (const auto &opt : *section) + formats.emplace(opt.key(), opt.value()); + + return formats; +} + +bool Config::isVerbose() const noexcept +{ + return util::isBoolean(get(m_document, "logs", "verbose")); +} + +bool Config::isForeground() const noexcept +{ + return util::isBoolean(get(m_document, "general", "foreground")); +} + +std::string Config::pidfile() const +{ + return get(m_document, "general", "pidfile"); +} + +std::string Config::uid() const +{ + return get(m_document, "general", "uid"); +} + +std::string Config::gid() const +{ + return get(m_document, "general", "gid"); +} + +void Config::loadLogs() const +{ + ini::Document::const_iterator sc = m_document.find("logs"); + + if (sc == m_document.end()) + return; + + ini::Section::const_iterator it; + + if ((it = sc->find("type")) != sc->end()) { + std::unique_ptr<log::Logger> iface; + + // Console is the default, no test case. + if (it->value() == "file") + iface = loadLogFile(*sc); + else if (it->value() == "syslog") + iface = loadLogSyslog(); + else + throw std::runtime_error("logs: unknown log type: {}"_format(it->value())); + + if (iface) + log::setLogger(std::move(iface)); + } +} + +void Config::loadFormats() const +{ + ini::Document::const_iterator sc = m_document.find("format"); + + if (sc == m_document.end()) + return; + + std::unique_ptr<IrccdLogFilter> filter = std::make_unique<IrccdLogFilter>(); + ini::Section::const_iterator it; + + if ((it = sc->find("debug")) != sc->cend()) + filter->m_debug = it->value(); + if ((it = sc->find("info")) != sc->cend()) + filter->m_info = it->value(); + if ((it = sc->find("warning")) != sc->cend()) + filter->m_warning = it->value(); + + log::setFilter(std::move(filter)); +} + +std::vector<std::shared_ptr<TransportServer>> Config::loadTransports() const +{ + std::vector<std::shared_ptr<TransportServer>> transports; + + for (const auto §ion : m_document) + if (section.key() == "transport") + transports.push_back(loadTransport(section)); + + return transports; +} + +std::vector<Rule> Config::loadRules() const +{ + std::vector<Rule> rules; + + for (const auto §ion : m_document) + if (section.key() == "rule") + rules.push_back(loadRule(section)); + + return rules; +} + +std::vector<std::shared_ptr<Server>> Config::loadServers() const +{ + std::vector<std::shared_ptr<Server>> servers; + + for (const auto §ion : m_document) { + if (section.key() != "server") + continue; + + try { + servers.push_back(loadServer(section, *this)); + } catch (const std::exception &ex) { + log::warning(ex.what()); + } + } + + return servers; +} + +void Config::loadPlugins(Irccd &irccd) const +{ + auto it = m_document.find("plugins"); + + if (it != m_document.end()) { + for (const auto &option : *it) { + if (!util::isIdentifierValid(option.key())) + continue; + + irccd.plugins().setConfig(option.key(), findPluginConfig(option.key())); + irccd.plugins().setFormats(option.key(), findPluginFormats(option.key())); + irccd.plugins().load(option.key(), option.value()); + } + } +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/config.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,184 @@ +/* + * config.hpp -- irccd configuration loader + * + * Copyright (c) 2013-2016 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_CONFIG_HPP +#define IRCCD_CONFIG_HPP + +/** + * \file config.hpp + * \brief Read .ini configuration file for irccd + */ + +#include <memory> +#include <string> +#include <vector> + +#include "ini.hpp" +#include "plugin.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +class Irccd; +class Rule; +class Server; +class TransportServer; + +/** + * \class Config + * \brief Read .ini configuration file for irccd + */ +class Config { +private: + std::string m_path; + ini::Document m_document; + +public: + /** + * Search the configuration file into the standard defined paths. + * + * \return the config + * \throw std::exception on errors or if no config could be found + */ + IRCCD_EXPORT static Config find(); + + /** + * Load the configuration from the specified path. + * + * \param path the path + */ + inline Config(std::string path) + : m_path(std::move(path)) + , m_document(ini::readFile(m_path)) + { + } + + /** + * Get the path to the configuration file. + * + * \return the path + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Find an entity if defined in the configuration file. + * + * \pre util::isValidIdentifier(name) + * \param server the server to update + * \param name the identity name + * \return default identity if cannot be found + */ + IRCCD_EXPORT void loadServerIdentity(Server &server, const std::string &name) const; + + /** + * Find a plugin configuration if defined in the configuration file. + * + * \pre util::isValidIdentifier(name) + * \return the configuration or empty if not found + */ + IRCCD_EXPORT PluginConfig findPluginConfig(const std::string &name) const; + + /** + * Find plugin formats if defined. + * + * \pre util::isValidIdentifier(name) + * \return the formats or empty one if not found + */ + IRCCD_EXPORT PluginFormats findPluginFormats(const std::string &name) const; + + /** + * Get the path to the pidfile. + * + * \return the path or empty if not defined + */ + IRCCD_EXPORT std::string pidfile() const; + + /** + * Get the uid. + * + * \return the uid or empty one if no one is set + */ + IRCCD_EXPORT std::string uid() const; + + /** + * Get the gid. + * + * \return the gid or empty one if no one is set + */ + IRCCD_EXPORT std::string gid() const; + + /** + * Check if verbosity is enabled. + * + * \return true if verbosity was requested + */ + IRCCD_EXPORT bool isVerbose() const noexcept; + + /** + * Check if foreground is specified (= no daemonize). + * + * \return true if foreground was requested + */ + IRCCD_EXPORT bool isForeground() const noexcept; + + /** + * Load logging interface. + */ + IRCCD_EXPORT void loadLogs() const; + + /** + * Load formats for logging. + */ + IRCCD_EXPORT void loadFormats() const; + + /** + * Load transports. + * + * \return the set of transports + */ + IRCCD_EXPORT std::vector<std::shared_ptr<TransportServer>> loadTransports() const; + + /** + * Load rules. + * + * \return the rules + */ + IRCCD_EXPORT std::vector<Rule> loadRules() const; + + /** + * Get the list of servers defined. + * + * \return the list of servers + */ + IRCCD_EXPORT std::vector<std::shared_ptr<Server>> loadServers() const; + + /** + * Get the list of defined plugins. + * + * \param irccd the irccd instance + * \return the list of plugins + */ + IRCCD_EXPORT void loadPlugins(Irccd &irccd) const; +}; + +} // !irccd + +#endif // !IRCCD_CONFIG_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/dynlib.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,338 @@ +/* + * dynlib.hpp -- portable shared library loader + * + * Copyright (c) 2013-2016 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_DYNLIB_HPP +#define IRCCD_DYNLIB_HPP + +/** + * \file dynlib.hpp + * \brief Portable shared library loader. + * \author David Demelier <markand@malikania.fr> + */ + +/** + * \page Dynlib Dynlib + * \brief Portable shared library loader. + * + * The dynlib module let you open shared libraries dynamically at runtime. + * + * ## Operating system support + * + * | System | Support | Remarks | + * |---------|---------|--------------------| + * | Apple | Ok | | + * | FreeBSD | Ok | | + * | Linux | Ok | Needs -ldl library | + * | Windows | Ok | | + * + * ## How to export symbols + * + * When you want to dynamically load symbols from your shared library, make sure they are in a `extern "C"` block, if + * not they will be [mangled][name-mangling]. + * + * Note, this does not mean that you can't write C++ code, it just mean that you can't use namespaces and function + * overloading. + * + * Example of **plugin.cpp**: + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * extern "C" { + * + * DYNLIB_EXPORT void plugin_load() + * { + * std::cout << "Loading plugin" << std::endl; + * } + * + * DYNLIB_EXPORT void plugin_unload() + * { + * std::cout << "Unloading plugin" << std::endl; + * } + * + * } + * ```` + * + * The \ref DYNLIB_EXPORT macro is necessary on some platforms to be sure that symbol will be visible. Make sure you always + * add it before any function. + * + * To compile, see your compiler documentation or build system. For gcc you can use the following: + * + * ```` + * gcc -std=c++14 -shared plugin.cpp -o plugin.so + * ```` + * + * ## How to load the library + * + * The dynlib module will search for the library in various places, thus you can use relative paths names but be sure + * that the library can be found. Otherwise, just use an absolute path to the file. + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * int main() + * { + * try { + * Dynlib dso("./plugin" DYNLIB_SUFFIX); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * + * return 0; + * } + * ```` + * + * ## How to load symbol + * + * The last part is symbol loading, you muse use raw C function pointer and the Dynlib::sym function. + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * using PluginLoad = void (*)(); + * using PluginUnload = void (*)(); + * + * int main() + * { + * try { + * Dynlib dso("./plugin" DYNLIB_SUFFIX); + * + * dso.sym<PluginLoad>("plugin_load")(); + * dso.sym<PluginUnload>("plugin_unload")(); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * + * return 0; + * } + * ```` + * + * [name-mangling]: https://en.wikipedia.org/wiki/Name_mangling + */ + +#include <stdexcept> +#include <string> + +#if defined(_WIN32) +# include <windows.h> +#else +# include <dlfcn.h> +#endif + +/** + * \brief Export the symbol. + * + * This is required on some platforms and you should put it before your function signature. + * + * \code{.cpp} + * extern "C" { + * + * DYNLIB_EXPORT void my_function() + * { + * } + * + * } + * \endcode + */ +#if defined(_WIN32) +# define DYNLIB_EXPORT __declspec(dllexport) +#else +# define DYNLIB_EXPORT +#endif + +/** + * \brief Usual suffix for the library. + * + * This macro expands to the suffix convention for this platform. + * + * \code{.cpp} + * Dynlib library("./myplugin" DYNLIB_SUFFIX); + * \endcode + * + * \note Don't use the suffix expanded value shown in Doxygen as it may be wrong. + */ +#if defined(_WIN32) +# define DYNLIB_SUFFIX ".dll" +#elif defined(__APPLE__) +# define DYNLIB_SUFFIX ".dylib" +#else +# define DYNLIB_SUFFIX ".so" +#endif + +namespace irccd { + +/** + * \class Dynlib + * \brief Load a dynamic module. + * + * This class is a portable wrapper to load shared libraries on supported systems. + */ +class Dynlib { +private: +#if defined(_WIN32) + using Handle = HMODULE; + using Sym = FARPROC; +#else + using Handle = void *; + using Sym = void *; +#endif + +public: + /** + * \brief Policy for symbol resolution. + */ + enum Policy { + Immediately, //!< load symbols immediately + Lazy //!< load symbols when needed + }; + +private: + Handle m_handle; + + Dynlib(const Dynlib &) = delete; + Dynlib &operator=(const Dynlib &) = delete; + + Dynlib(Dynlib &&) = delete; + Dynlib &operator=(Dynlib &&) = delete; + +#if defined(_WIN32) + std::string error() + { + LPSTR error = nullptr; + std::string errmsg; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, NULL); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; + } +#endif + +public: + /** + * Constructor to load a shared module. + * + * \param path the absolute path + * \param policy the policy to load + * \throw std::runtime_error on error + */ + inline Dynlib(const std::string &path, Policy policy = Immediately); + + /** + * Close the library automatically. + */ + inline ~Dynlib(); + + /** + * Get a symbol from the library. + * + * On some platforms the symbol must be manually exported. + * + * \param name the symbol + * \return the symbol + * \throw std::runtime_error on error + * \see DYNLIB_EXPORT + */ + template <typename T> + inline T sym(const std::string &name); +}; + +#if defined(_WIN32) + +/* + * Windows implementation + * ------------------------------------------------------------------ + */ + +Dynlib::Dynlib(const std::string &path, Policy) +{ + m_handle = LoadLibraryA(path.c_str()); + + if (m_handle == nullptr) + throw std::runtime_error(error()); +} + +Dynlib::~Dynlib() +{ + FreeLibrary(m_handle); + m_handle = nullptr; +} + +template <typename T> +T Dynlib::sym(const std::string &name) +{ + Sym sym = GetProcAddress(m_handle, name.c_str()); + + if (sym == nullptr) + throw std::runtime_error(error()); + + return reinterpret_cast<T>(sym); +} + +#else + +/* + * Unix implementation + * ------------------------------------------------------------------ + */ + +Dynlib::Dynlib(const std::string &path, Policy policy) +{ + m_handle = dlopen(path.c_str(), policy == Immediately ? RTLD_NOW : RTLD_LAZY); + + if (m_handle == nullptr) + throw std::runtime_error(dlerror()); +} + +Dynlib::~Dynlib() +{ + dlclose(m_handle); + m_handle = nullptr; +} + +template <typename T> +T Dynlib::sym(const std::string &name) +{ + Sym sym = dlsym(m_handle, name.c_str()); + + if (sym == nullptr) + throw std::runtime_error(dlerror()); + + return reinterpret_cast<T>(sym); +} + +#endif + +#endif // !IRCCD_DYNLIB_HPP + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/irccd.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,102 @@ +/* + * irccd.cpp -- main irccd class + * + * Copyright (c) 2013-2016 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.hpp" +#include "logger.hpp" +#include "net.hpp" +#include "service-command.hpp" +#include "service-interrupt.hpp" +#include "service-plugin.hpp" +#include "service-rule.hpp" +#include "service-server.hpp" +#include "service-transport.hpp" +#include "util.hpp" + +using namespace std; +using namespace std::placeholders; +using namespace std::string_literals; + +namespace irccd { + +Irccd::Irccd() + : m_commandService(std::make_shared<CommandService>()) + , m_interruptService(std::make_shared<InterruptService>()) + , m_servers(std::make_shared<ServerService>(*this)) + , m_transports(std::make_shared<TransportService>(*this)) + , m_ruleService(std::make_shared<RuleService>()) + , m_plugins(std::make_shared<PluginService>(*this)) +{ +} + +void Irccd::post(std::function<void (Irccd &)> ev) noexcept +{ + std::lock_guard<mutex> lock(m_mutex); + + m_events.push_back(move(ev)); + m_interruptService->interrupt(); +} + +void Irccd::run() +{ + while (m_running) { + util::poller::poll(250, *m_interruptService, *m_servers, *m_transports); + dispatch(); + } +} + +void Irccd::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + util::poller::prepare(in, out, max, *m_interruptService, *m_servers, *m_transports); +} + +void Irccd::sync(fd_set &in, fd_set &out) +{ + util::poller::sync(in, out, *m_interruptService, *m_servers, *m_transports); +} + +void Irccd::dispatch() +{ + /* + * Make a copy because the events can add other events while we are + * iterating it. Also lock because the timers may alter these events too. + */ + std::vector<std::function<void (Irccd &)>> copy; + + { + std::lock_guard<mutex> lock(m_mutex); + + copy = move(m_events); + m_events.clear(); + } + + if (copy.size() > 0) + log::debug() << "irccd: dispatching " << copy.size() << " event" << (copy.size() > 1 ? "s" : "") << endl; + + for (auto &ev : copy) + ev(*this); +} + +void Irccd::stop() +{ + log::debug() << "irccd: requesting to stop now" << endl; + + m_running = false; + m_interruptService->interrupt(); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/irccd.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,174 @@ +/* + * irccd.hpp -- main irccd class + * + * Copyright (c) 2013-2016 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_HPP +#define IRCCD_HPP + +/** + * \file irccd.hpp + * \brief Base class for irccd front end. + */ + +#include <atomic> +#include <functional> +#include <memory> +#include <mutex> +#include <vector> + +#include "net.hpp" +#include "sysconfig.hpp" + +/** + * \brief Main irccd namespace + */ +namespace irccd { + +class CommandService; +class InterruptService; +class PluginService; +class RuleService; +class ServerService; +class TransportService; + +/** + * \class Irccd + * \brief Irccd main instance. + */ +class Irccd { +private: + // Main loop stuff. + std::atomic<bool> m_running{true}; + std::mutex m_mutex; + std::vector<std::function<void (Irccd &)>> m_events; + + // Services. + std::shared_ptr<CommandService> m_commandService; + std::shared_ptr<InterruptService> m_interruptService; + std::shared_ptr<ServerService> m_servers; + std::shared_ptr<TransportService> m_transports; + std::shared_ptr<RuleService> m_ruleService; + std::shared_ptr<PluginService> m_plugins; + + // Not copyable and not movable because services has references to irccd. + Irccd(const Irccd &) = delete; + Irccd(Irccd &&) = delete; + + Irccd &operator=(const Irccd &) = delete; + Irccd &operator=(Irccd &&) = delete; + +public: + /** + * Prepare standard services. + */ + IRCCD_EXPORT Irccd(); + + /** + * Access the command service. + * + * \return the service + */ + inline CommandService &commands() noexcept + { + return *m_commandService; + } + + /** + * Access the server service. + * + * \return the service + */ + inline ServerService &servers() noexcept + { + return *m_servers; + } + + /** + * Access the transport service. + * + * \return the service + */ + inline TransportService &transports() noexcept + { + return *m_transports; + } + + /** + * Access the rule service. + * + * \return the service + */ + inline RuleService &rules() noexcept + { + return *m_ruleService; + } + + /** + * Access the plugin service. + * + * \return the service + */ + inline PluginService &plugins() noexcept + { + return *m_plugins; + } + + /** + * Prepare the services for selection. + * + * \param in the input set + * \param out the output set + * \param max the maximum handle + */ + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * Synchronize the services. + * + * \param in the input set + * \param out the output set + */ + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); + + /** + * Add an event to the queue. This will immediately signals the event loop + * to interrupt itself to dispatch the pending events. + * + * \param ev the event + * \note Thread-safe + */ + IRCCD_EXPORT void post(std::function<void (Irccd &)> ev) noexcept; + + /** + * Loop forever by calling poll() and dispatch() indefinitely. + */ + IRCCD_EXPORT void run(); + + /** + * Dispatch the pending events, usually after calling poll(). + */ + IRCCD_EXPORT void dispatch(); + + /** + * Request to stop, usually from a signal. + */ + IRCCD_EXPORT void stop(); +}; + +} // !irccd + +#endif // !IRCCD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/plugin-dynlib.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,210 @@ +/* + * plugin-dynlib.cpp -- native plugin implementation + * + * Copyright (c) 2013-2016 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 "fs.hpp" +#include "logger.hpp" +#include "path.hpp" +#include "plugin-dynlib.hpp" + +namespace irccd { + +namespace { + +template <typename Sym> +inline Sym sym(Dynlib &dynlib, const std::string &name) +{ + try { + return dynlib.sym<Sym>(name); + } catch (...) { + return nullptr; + } +} + +template <typename Sym, typename... Args> +inline void call(Sym sym, Args&&... args) +{ + if (sym) + sym(std::forward<Args>(args)...); +} + +} // !namespace + +DynlibPlugin::DynlibPlugin(std::string name, std::string path) + : Plugin(name, path) + , m_dso(std::move(path)) + , m_onCommand(sym<OnCommand>(m_dso, "irccd_onCommand")) + , m_onConnect(sym<OnConnect>(m_dso, "irccd_onConnect")) + , m_onChannelMode(sym<OnChannelMode>(m_dso, "irccd_onChannelMode")) + , m_onChannelNotice(sym<OnChannelNotice>(m_dso, "irccd_onChannelNotice")) + , m_onInvite(sym<OnInvite>(m_dso, "irccd_onInvite")) + , m_onJoin(sym<OnJoin>(m_dso, "irccd_onJoin")) + , m_onKick(sym<OnKick>(m_dso, "irccd_onKick")) + , m_onLoad(sym<OnLoad>(m_dso, "irccd_onLoad")) + , m_onMessage(sym<OnMessage>(m_dso, "irccd_onMessage")) + , m_onMe(sym<OnMe>(m_dso, "irccd_onMe")) + , m_onMode(sym<OnMode>(m_dso, "irccd_onMode")) + , m_onNames(sym<OnNames>(m_dso, "irccd_onNames")) + , m_onNick(sym<OnNick>(m_dso, "irccd_onNick")) + , m_onNotice(sym<OnNotice>(m_dso, "irccd_onNotice")) + , m_onPart(sym<OnPart>(m_dso, "irccd_onPart")) + , m_onQuery(sym<OnQuery>(m_dso, "irccd_onQuery")) + , m_onQueryCommand(sym<OnQueryCommand>(m_dso, "irccd_onQueryCommand")) + , m_onReload(sym<OnReload>(m_dso, "irccd_onReload")) + , m_onTopic(sym<OnTopic>(m_dso, "irccd_onTopic")) + , m_onUnload(sym<OnUnload>(m_dso, "irccd_onUnload")) + , m_onWhois(sym<OnWhois>(m_dso, "irccd_onWhois")) +{ +} + +void DynlibPlugin::onCommand(Irccd &irccd, const MessageEvent &ev) +{ + call(m_onCommand, irccd, ev); +} + +void DynlibPlugin::onConnect(Irccd &irccd, const ConnectEvent &ev) +{ + call(m_onConnect, irccd, ev); +} + +void DynlibPlugin::onChannelMode(Irccd &irccd, const ChannelModeEvent &ev) +{ + call(m_onChannelMode, irccd, ev); +} + +void DynlibPlugin::onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &ev) +{ + call(m_onChannelNotice, irccd, ev); +} + +void DynlibPlugin::onInvite(Irccd &irccd, const InviteEvent &ev) +{ + call(m_onInvite, irccd, ev); +} + +void DynlibPlugin::onJoin(Irccd &irccd, const JoinEvent &ev) +{ + call(m_onJoin, irccd, ev); +} + +void DynlibPlugin::onKick(Irccd &irccd, const KickEvent &ev) +{ + call(m_onKick, irccd, ev); +} + +void DynlibPlugin::onLoad(Irccd &irccd) +{ + call(m_onLoad, irccd, *this); +} + +void DynlibPlugin::onMessage(Irccd &irccd, const MessageEvent &ev) +{ + call(m_onMessage, irccd, ev); +} + +void DynlibPlugin::onMe(Irccd &irccd, const MeEvent &ev) +{ + call(m_onMe, irccd, ev); +} + +void DynlibPlugin::onMode(Irccd &irccd, const ModeEvent &ev) +{ + call(m_onMode, irccd, ev); +} + +void DynlibPlugin::onNames(Irccd &irccd, const NamesEvent &ev) +{ + call(m_onNames, irccd, ev); +} + +void DynlibPlugin::onNick(Irccd &irccd, const NickEvent &ev) +{ + call(m_onNick, irccd, ev); +} + +void DynlibPlugin::onNotice(Irccd &irccd, const NoticeEvent &ev) +{ + call(m_onNotice, irccd, ev); +} + +void DynlibPlugin::onPart(Irccd &irccd, const PartEvent &ev) +{ + call(m_onPart, irccd, ev); +} + +void DynlibPlugin::onQuery(Irccd &irccd, const QueryEvent &ev) +{ + call(m_onQuery, irccd, ev); +} + +void DynlibPlugin::onQueryCommand(Irccd &irccd, const QueryEvent &ev) +{ + call(m_onQueryCommand, irccd, ev); +} + +void DynlibPlugin::onReload(Irccd &irccd) +{ + call(m_onReload, irccd, *this); +} + +void DynlibPlugin::onTopic(Irccd &irccd, const TopicEvent &ev) +{ + call(m_onTopic, irccd, ev); +} + +void DynlibPlugin::onUnload(Irccd &irccd) +{ + call(m_onUnload, irccd, *this); +} + +void DynlibPlugin::onWhois(Irccd &irccd, const WhoisEvent &ev) +{ + call(m_onWhois, irccd, ev); +} + +std::shared_ptr<Plugin> DynlibPluginLoader::open(const std::string &id, + const std::string &path) noexcept +{ + if (path.rfind(DYNLIB_SUFFIX) == std::string::npos) + return nullptr; + + try { + return std::make_shared<DynlibPlugin>(id, path); + } catch (const std::exception &ex) { + log::warning() << "plugin " << id << ": " << ex.what() << std::endl; + } + + return nullptr; +} + +std::shared_ptr<Plugin> DynlibPluginLoader::find(const std::string &id) noexcept +{ + for (const auto &dir : path::list(path::PathNativePlugins)) { + auto path = dir + id + DYNLIB_SUFFIX; + + if (!fs::isReadable(path)) + continue; + + log::info() << "plugin " << id << ": trying " << path << std::endl; + + return open(id, path); + } + + return nullptr; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/plugin-dynlib.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,222 @@ +/* + * plugin-dynlib.hpp -- native plugin implementation + * + * Copyright (c) 2013-2016 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_PLUGIN_DYNLIB_HPP +#define IRCCD_PLUGIN_DYNLIB_HPP + +/** + * \file plugin-dynlib.hpp + * \brief Native plugin implementation. + */ + +#include "dynlib.hpp" +#include "plugin.hpp" + +namespace irccd { + +/** + * \brief Dynlib based plugin. + * \ingroup plugins + */ +class DynlibPlugin : public Plugin { +private: + using OnCommand = void (*)(Irccd &, const MessageEvent &); + using OnConnect = void (*)(Irccd &, const ConnectEvent &); + using OnChannelMode = void (*)(Irccd &, const ChannelModeEvent &); + using OnChannelNotice = void (*)(Irccd &, const ChannelNoticeEvent &); + using OnInvite = void (*)(Irccd &, const InviteEvent &); + using OnJoin = void (*)(Irccd &, const JoinEvent &); + using OnKick = void (*)(Irccd &, const KickEvent &); + using OnLoad = void (*)(Irccd &, DynlibPlugin &); + using OnMessage = void (*)(Irccd &, const MessageEvent &); + using OnMe = void (*)(Irccd &, const MeEvent &); + using OnMode = void (*)(Irccd &, const ModeEvent &); + using OnNames = void (*)(Irccd &, const NamesEvent &); + using OnNick = void (*)(Irccd &, const NickEvent &); + using OnNotice = void (*)(Irccd &, const NoticeEvent &); + using OnPart = void (*)(Irccd &, const PartEvent &); + using OnQuery = void (*)(Irccd &, const QueryEvent &); + using OnQueryCommand = void (*)(Irccd &, const QueryEvent &); + using OnReload = void (*)(Irccd &, DynlibPlugin &); + using OnTopic = void (*)(Irccd &, const TopicEvent &); + using OnUnload = void (*)(Irccd &, DynlibPlugin &); + using OnWhois = void (*)(Irccd &, const WhoisEvent &); + + Dynlib m_dso; + OnCommand m_onCommand; + OnConnect m_onConnect; + OnChannelMode m_onChannelMode; + OnChannelNotice m_onChannelNotice; + OnInvite m_onInvite; + OnJoin m_onJoin; + OnKick m_onKick; + OnLoad m_onLoad; + OnMessage m_onMessage; + OnMe m_onMe; + OnMode m_onMode; + OnNames m_onNames; + OnNick m_onNick; + OnNotice m_onNotice; + OnPart m_onPart; + OnQuery m_onQuery; + OnQueryCommand m_onQueryCommand; + OnReload m_onReload; + OnTopic m_onTopic; + OnUnload m_onUnload; + OnWhois m_onWhois; + + // Configuration and formats. + PluginConfig m_config; + PluginFormats m_formats; + +public: + /** + * Construct the plugin. + * + * \param name the name + * \param path the fully resolved path (must be absolute) + * \throw std::exception on failures + */ + DynlibPlugin(std::string name, std::string path); + + /** + * \copydoc Plugin::onCommand + */ + IRCCD_EXPORT void onCommand(Irccd &irccd, const MessageEvent &event) override; + + /** + * \copydoc Plugin::onConnect + */ + IRCCD_EXPORT void onConnect(Irccd &irccd, const ConnectEvent &event) override; + + /** + * \copydoc Plugin::onChannelMode + */ + IRCCD_EXPORT void onChannelMode(Irccd &irccd, const ChannelModeEvent &event) override; + + /** + * \copydoc Plugin::onChannelNotice + */ + IRCCD_EXPORT void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event) override; + + /** + * \copydoc Plugin::onInvite + */ + IRCCD_EXPORT void onInvite(Irccd &irccd, const InviteEvent &event) override; + + /** + * \copydoc Plugin::onJoin + */ + IRCCD_EXPORT void onJoin(Irccd &irccd, const JoinEvent &event) override; + + /** + * \copydoc Plugin::onKick + */ + IRCCD_EXPORT void onKick(Irccd &irccd, const KickEvent &event) override; + + /** + * \copydoc Plugin::onLoad + */ + IRCCD_EXPORT void onLoad(Irccd &irccd) override; + + /** + * \copydoc Plugin::onMessage + */ + IRCCD_EXPORT void onMessage(Irccd &irccd, const MessageEvent &event) override; + + /** + * \copydoc Plugin::onMe + */ + IRCCD_EXPORT void onMe(Irccd &irccd, const MeEvent &event) override; + + /** + * \copydoc Plugin::onMode + */ + IRCCD_EXPORT void onMode(Irccd &irccd, const ModeEvent &event) override; + + /** + * \copydoc Plugin::onNames + */ + IRCCD_EXPORT void onNames(Irccd &irccd, const NamesEvent &event) override; + + /** + * \copydoc Plugin::onNick + */ + IRCCD_EXPORT void onNick(Irccd &irccd, const NickEvent &event) override; + + /** + * \copydoc Plugin::onNotice + */ + IRCCD_EXPORT void onNotice(Irccd &irccd, const NoticeEvent &event) override; + + /** + * \copydoc Plugin::onPart + */ + IRCCD_EXPORT void onPart(Irccd &irccd, const PartEvent &event) override; + + /** + * \copydoc Plugin::onQuery + */ + IRCCD_EXPORT void onQuery(Irccd &irccd, const QueryEvent &event) override; + + /** + * \copydoc Plugin::onQueryCommand + */ + IRCCD_EXPORT void onQueryCommand(Irccd &irccd, const QueryEvent &event) override; + + /** + * \copydoc Plugin::onReload + */ + IRCCD_EXPORT void onReload(Irccd &irccd) override; + + /** + * \copydoc Plugin::onTopic + */ + IRCCD_EXPORT void onTopic(Irccd &irccd, const TopicEvent &event) override; + + /** + * \copydoc Plugin::onUnload + */ + IRCCD_EXPORT void onUnload(Irccd &irccd) override; + + /** + * \copydoc Plugin::onWhois + */ + IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override; +}; + +/** + * \brief Implementation for searching native plugins. + */ +class DynlibPluginLoader : public PluginLoader { +public: + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr<Plugin> open(const std::string &id, + const std::string &path) noexcept override; + + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr<Plugin> find(const std::string &id) noexcept override; +}; + +} // !irccd + +#endif // !IRCCD_PLUGIN_DYNLIB_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/plugin.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,500 @@ +/* + * plugin.hpp -- irccd JavaScript plugin interface + * + * Copyright (c) 2013-2016 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_PLUGIN_HPP +#define IRCCD_PLUGIN_HPP + +/** + * \file plugin.hpp + * \brief Irccd plugins + */ + +/** + * \defgroup plugins Plugins + * \brief Plugin management. + */ + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +#include "server.hpp" +#include "sysconfig.hpp" +#include "util.hpp" + +namespace irccd { + +class Irccd; + +/** + * \brief Configuration map extract from config file. + */ +using PluginConfig = std::unordered_map<std::string, std::string>; + +/** + * \brief Formats for plugins. + */ +using PluginFormats = std::unordered_map<std::string, std::string>; + +/** + * \ingroup plugins + * \brief Abstract plugin. + * + * A plugin is identified by name and can be loaded and unloaded at runtime. + */ +class Plugin : public std::enable_shared_from_this<Plugin> { +private: + // Plugin information + std::string m_name; + std::string m_path; + + // Metadata + std::string m_author{"unknown"}; + std::string m_license{"unknown"}; + std::string m_summary{"unknown"}; + std::string m_version{"unknown"}; + +public: + /** + * Constructor. + * + * \param name the plugin id + * \param path the fully resolved path to the plugin + * \throws std::runtime_error on errors + */ + inline Plugin(std::string name, std::string path) noexcept + : m_name(std::move(name)) + , m_path(std::move(path)) + { + } + + /** + * Temporary, close all timers. + */ + virtual ~Plugin() = default; + + /** + * Get the plugin name. + * + * \return the plugin name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Get the plugin path. + * + * \return the plugin path + * \note some plugins may not exist on the disk + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Get the author. + * + * \return the author + */ + inline const std::string &author() const noexcept + { + return m_author; + } + + /** + * Set the author. + * + * \param author the author + */ + inline void setAuthor(std::string author) noexcept + { + m_author = std::move(author); + } + + /** + * Get the license. + * + * \return the license + */ + inline const std::string &license() const noexcept + { + return m_license; + } + + /** + * Set the license. + * + * \param license the license + */ + inline void setLicense(std::string license) noexcept + { + m_license = std::move(license); + } + + /** + * Get the summary. + * + * \return the summary + */ + inline const std::string &summary() const noexcept + { + return m_summary; + } + + /** + * Set the summary. + * + * \param summary the summary + */ + inline void setSummary(std::string summary) noexcept + { + m_summary = std::move(summary); + } + + /** + * Get the version. + * + * \return the version + */ + inline const std::string &version() const noexcept + { + return m_version; + } + + /** + * Set the version. + * + * \param version the version + */ + inline void setVersion(std::string version) noexcept + { + m_version = std::move(version); + } + + /** + * Access the plugin configuration. + * + * \return the config + */ + virtual PluginConfig config() + { + return {}; + } + + /** + * Set the configuration. + * + * \param config the configuration + */ + virtual void setConfig(PluginConfig config) + { + util::unused(config); + } + + /** + * Access the plugin formats. + * + * \return the format + */ + virtual PluginFormats formats() + { + return {}; + } + + /** + * Set the formats. + * + * \param formats the formats + */ + virtual void setFormats(PluginFormats formats) + { + util::unused(formats); + } + + /** + * On channel message. This event will call onMessage or + * onCommand if the messages starts with the command character + * plus the plugin name. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onCommand(Irccd &irccd, const MessageEvent &event) + { + util::unused(irccd, event); + } + + /** + * On successful connection. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onConnect(Irccd &irccd, const ConnectEvent &event) + { + util::unused(irccd, event); + } + + /** + * On channel mode. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onChannelMode(Irccd &irccd, const ChannelModeEvent &event) + { + util::unused(irccd, event); + } + + /** + * On a channel notice. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event) + { + util::unused(irccd, event); + } + + /** + * On invitation. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onInvite(Irccd &irccd, const InviteEvent &event) + { + util::unused(irccd, event); + } + + /** + * On join. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onJoin(Irccd &irccd, const JoinEvent &event) + { + util::unused(irccd, event); + } + + /** + * On kick. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onKick(Irccd &irccd, const KickEvent &event) + { + util::unused(irccd, event); + } + + /** + * On load. + * + * \param irccd the irccd instance + */ + virtual void onLoad(Irccd &irccd) + { + util::unused(irccd); + } + + /** + * On channel message. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onMessage(Irccd &irccd, const MessageEvent &event) + { + util::unused(irccd, event); + } + + /** + * On CTCP Action. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onMe(Irccd &irccd, const MeEvent &event) + { + util::unused(irccd, event); + } + + /** + * On user mode change. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onMode(Irccd &irccd, const ModeEvent &event) + { + util::unused(irccd, event); + } + + /** + * On names listing. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onNames(Irccd &irccd, const NamesEvent &event) + { + util::unused(irccd, event); + } + + /** + * On nick change. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onNick(Irccd &irccd, const NickEvent &event) + { + util::unused(irccd, event); + } + + /** + * On user notice. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onNotice(Irccd &irccd, const NoticeEvent &event) + { + util::unused(irccd, event); + } + + /** + * On part. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onPart(Irccd &irccd, const PartEvent &event) + { + util::unused(irccd, event); + } + + /** + * On user query. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onQuery(Irccd &irccd, const QueryEvent &event) + { + util::unused(irccd, event); + } + + /** + * On user query command. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onQueryCommand(Irccd &irccd, const QueryEvent &event) + { + util::unused(irccd, event); + } + + /** + * On reload. + * + * \param irccd the irccd instance + */ + virtual void onReload(Irccd &irccd) + { + util::unused(irccd); + } + + /** + * On topic change. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onTopic(Irccd &irccd, const TopicEvent &event) + { + util::unused(irccd, event); + } + + /** + * On unload. + * + * \param irccd the irccd instance + */ + virtual void onUnload(Irccd &irccd) + { + util::unused(irccd); + } + + /** + * On whois information. + * + * \param irccd the irccd instance + * \param event the event + */ + virtual void onWhois(Irccd &irccd, const WhoisEvent &event) + { + util::unused(irccd, event); + } +}; + +/** + * \brief Abstract interface for searching plugins. + * + * This class is used to make loading of plugins extensible, the PluginService + * knows some predefined plugins loaders and use them to search for available + * plugins. + * + * This makes easier to implement new plugins or new ways of loading them. + * + * \see DynlibPluginLoader + * \see JsPluginLoader + */ +class PluginLoader { +public: + /** + * Try to open the plugin specified by path. + * + * The implementation must test if the plugin is suitable for opening, by + * testing extension for example. + * + * \param file the file + */ + virtual std::shared_ptr<Plugin> open(const std::string &id, + const std::string &file) noexcept = 0; + + /** + * Search for a plugin named by this id. + * + * \param id the plugin id + * \return the plugin + */ + virtual std::shared_ptr<Plugin> find(const std::string &id) noexcept = 0; +}; + +} // !irccd + +#endif // !IRCCD_PLUGIN_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/rule.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,114 @@ +/* + * rule.cpp -- rule for server and channels + * + * Copyright (c) 2013-2016 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 <stdexcept> + +#include "logger.hpp" +#include "rule.hpp" +#include "util.hpp" + +using namespace std; + +namespace { + +const std::unordered_set<std::string> validEvents{ + "onChannelMode" + "onChannelNotice", + "onCommand", + "onConnect", + "onInvite", + "onJoin", + "onKick", + "onMessage", + "onMode", + "onNames", + "onNick", + "onNotice", + "onPart", + "onQuery", + "onQueryCommand", + "onTopic", + "onWhois" +}; + +} // !namespace + +namespace irccd { + +bool Rule::matchMap(const RuleSet &map, const std::string &value) const noexcept +{ + return value.empty() || map.empty() || map.count(value) == 1; +} + +Rule::Rule(RuleSet servers, RuleSet channels, RuleSet origins, RuleSet plugins, RuleSet events, RuleAction action) + : m_servers(std::move(servers)) + , m_channels(std::move(channels)) + , m_origins(std::move(origins)) + , m_plugins(std::move(plugins)) + , m_events(std::move(events)) + , m_action(action) +{ + for (const std::string &n : m_events) + if (validEvents.count(n) == 0) + throw std::invalid_argument(n + " is not a valid event name"); +} + +bool Rule::match(const std::string &server, + const std::string &channel, + const std::string &nick, + const std::string &plugin, + const std::string &event) const noexcept +{ + return matchMap(m_servers, server) && + matchMap(m_channels, channel) && + matchMap(m_origins, nick) && + matchMap(m_plugins, plugin) && + matchMap(m_events, event); +} + +RuleAction Rule::action() const noexcept +{ + return m_action; +} + +const RuleSet &Rule::servers() const noexcept +{ + return m_servers; +} + +const RuleSet &Rule::channels() const noexcept +{ + return m_channels; +} + +const RuleSet &Rule::origins() const noexcept +{ + return m_origins; +} + +const RuleSet &Rule::plugins() const noexcept +{ + return m_plugins; +} + +const RuleSet &Rule::events() const noexcept +{ + return m_events; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/rule.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,150 @@ +/* + * rule.hpp -- rule for server and channels + * + * Copyright (c) 2013-2016 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_RULE_HPP +#define IRCCD_RULE_HPP + +/** + * \file rule.hpp + * \brief Rule description + */ + +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include "sysconfig.hpp" + +namespace irccd { + +/** + * List of criterias. + */ +using RuleSet = std::unordered_set<std::string>; + +/** + * \enum RuleAction + * \brief Rule action + */ +enum class RuleAction { + Accept, //!< The event is accepted (default) + Drop //!< The event is dropped +}; + +/** + * \class Rule + * \brief Manage rule to activate or deactive events. + */ +class Rule { +private: + RuleSet m_servers; + RuleSet m_channels; + RuleSet m_origins; + RuleSet m_plugins; + RuleSet m_events; + RuleAction m_action{RuleAction::Accept}; + + /* + * Check if a map contains the value and return true if it is + * or return true if value is empty (which means applicable). + */ + bool matchMap(const RuleSet &map, const std::string &value) const noexcept; + +public: + /** + * Rule constructor. + * + * \param servers the server list + * \param channels the channels + * \param nicknames the nicknames + * \param plugins the plugins + * \param events the events + * \param action the rule action + * \throw std::invalid_argument if events are invalid + */ + IRCCD_EXPORT Rule(RuleSet servers = RuleSet{}, + RuleSet channels = RuleSet{}, + RuleSet nicknames = RuleSet{}, + RuleSet plugins = RuleSet{}, + RuleSet events = RuleSet{}, + RuleAction action = RuleAction::Accept); + + /** + * Check if that rule apply for the given criterias. + * + * \param server the server + * \param channel the channel + * \param nick the origin + * \param plugin the plugin + * \param event the event + * \return true if match + */ + IRCCD_EXPORT bool match(const std::string &server, + const std::string &channel, + const std::string &nick, + const std::string &plugin, + const std::string &event) const noexcept; + + /** + * Get the action. + * + * \return the action + */ + IRCCD_EXPORT RuleAction action() const noexcept; + + /** + * Get the servers. + * + * \return the servers + */ + IRCCD_EXPORT const RuleSet &servers() const noexcept; + + /** + * Get the channels. + * + * \return the channels + */ + IRCCD_EXPORT const RuleSet &channels() const noexcept; + + /** + * Get the origins. + * + * \return the origins + */ + IRCCD_EXPORT const RuleSet &origins() const noexcept; + + /** + * Get the plugins. + * + * \return the plugins + */ + IRCCD_EXPORT const RuleSet &plugins() const noexcept; + + /** + * Get the events. + * + * \return the events + */ + IRCCD_EXPORT const RuleSet &events() const noexcept; +}; + +} // !irccd + +#endif // !IRCCD_RULE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/server.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,910 @@ +/* + * server.cpp -- an IRC server + * + * Copyright (c) 2013-2016 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 <algorithm> +#include <cerrno> +#include <cstring> +#include <stdexcept> + +#include <format.h> + +#include <libircclient.h> +#include <libirc_rfcnumeric.h> + +#include "sysconfig.hpp" + +#if !defined(IRCCD_SYSTEM_WINDOWS) +# include <sys/types.h> +# include <netinet/in.h> +# include <arpa/nameser.h> +# include <resolv.h> +#endif + +#include "logger.hpp" +#include "util.hpp" +#include "server.hpp" + +using namespace fmt::literals; + +namespace irccd { + +/* + * Server::Session declaration. + * ------------------------------------------------------------------ + */ + +class Server::Session { +public: + std::unique_ptr<irc_session_t, void (*)(irc_session_t *)> m_handle{nullptr, nullptr}; + + inline operator const irc_session_t *() const noexcept + { + return m_handle.get(); + } + + inline operator irc_session_t *() noexcept + { + return m_handle.get(); + } + + inline bool isConnected() const noexcept + { + return irc_is_connected(m_handle.get()); + } +}; + +/* + * Server::State declaration. + * ------------------------------------------------------------------ + */ + +class Server::State { +public: + State() = default; + virtual ~State() = default; + virtual void prepare(Server &, fd_set &, fd_set &, net::Handle &) = 0; + virtual std::string ident() const = 0; +}; + +/* + * Server::DisconnectedState declaration. + * ------------------------------------------------------------------ + */ + +class Server::DisconnectedState : public Server::State { +private: + ElapsedTimer m_timer; + +public: + void prepare(Server &, fd_set &, fd_set &, net::Handle &) override; + std::string ident() const override; +}; + +/* + * Server::ConnectingState declaration. + * ------------------------------------------------------------------ + */ + +class Server::ConnectingState : public State { +private: + enum { + Disconnected, + Connecting + } m_state{Disconnected}; + + ElapsedTimer m_timer; + + bool connect(Server &server); + +public: + void prepare(Server &, fd_set &, fd_set &, net::Handle &) override; + std::string ident() const override; +}; + +/* + * Server::ConnectedState declaration. + * ------------------------------------------------------------------ + */ + +class Server::ConnectedState : public State { +public: + void prepare(Server &, fd_set &, fd_set &, net::Handle &) override; + std::string ident() const override; +}; + +namespace { + +/* + * strify + * ------------------------------------------------------------------ + * + * Make sure to build a C++ string with a not-null C string. + */ +inline std::string strify(const char *s) +{ + return (s == nullptr) ? "" : std::string(s); +} + +/* + * cleanPrefix + * ------------------------------------------------------------------ + * + * Remove the user prefix only if it is present in the mode table, for example + * removes @ from @irccd if and only if @ is a character mode (e.g. operator). + */ +std::string cleanPrefix(const std::map<ChannelMode, char> &modes, std::string nickname) +{ + if (nickname.length() == 0) + return nickname; + + for (const auto &pair : modes) + if (nickname[0] == pair.second) + nickname.erase(0, 1); + + return nickname; +} + +/* + * extractPrefixes + * ------------------------------------------------------------------ + * + * Read modes from the IRC event numeric. + */ +std::map<ChannelMode, char> extractPrefixes(const std::string &line) +{ + std::pair<char, char> table[16]; + std::string buf = line.substr(7); + std::map<ChannelMode, char> modes; + + for (int i = 0; i < 16; ++i) + table[i] = std::make_pair(-1, -1); + + int j = 0; + bool readModes = true; + for (size_t i = 0; i < buf.size(); ++i) { + if (buf[i] == '(') + continue; + if (buf[i] == ')') { + j = 0; + readModes = false; + continue; + } + + if (readModes) + table[j++].first = buf[i]; + else + table[j++].second = buf[i]; + } + + // Put these as a map of mode to prefix. + for (int i = 0; i < 16; ++i) { + auto key = static_cast<ChannelMode>(table[i].first); + auto value = table[i].second; + + modes.emplace(key, value); + } + + return modes; +} + +} // !namespace + +void Server::removeJoinedChannel(const std::string &channel) +{ + m_jchannels.erase(std::remove(m_jchannels.begin(), m_jchannels.end(), channel), m_jchannels.end()); +} + +void Server::handleConnect(const char *, const char **) noexcept +{ + // Reset the number of tried reconnection. + m_recocur = 1; + + // Reset the timer. + m_timer.reset(); + + // Reset joined channels. + m_jchannels.clear(); + + // Don't forget to change state and notify. + next(std::make_unique<ConnectedState>()); + onConnect(ConnectEvent{shared_from_this()}); + + // Auto join listed channels. + for (const auto &channel : m_rchannels) { + log::info() << "server " << m_name << ": auto joining " << channel.name << std::endl; + join(channel.name, channel.password); + } +} + +void Server::handleChannel(const char *orig, const char **params) noexcept +{ + onMessage(MessageEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); +} + +void Server::handleChannelMode(const char *orig, const char **params) noexcept +{ + onChannelMode(ChannelModeEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1]), strify(params[2])}); +} + +void Server::handleChannelNotice(const char *orig, const char **params) noexcept +{ + onChannelNotice(ChannelNoticeEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); +} + +void Server::handleCtcpAction(const char *orig, const char **params) noexcept +{ + onMe(MeEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); +} + +void Server::handleInvite(const char *orig, const char **params) noexcept +{ + // If joininvite is set, join the channel. + if ((m_flags & JoinInvite) && isSelf(strify(params[0]))) + join(strify(params[1])); + + /* + * The libircclient says that invite contains the target nickname, it's + * quit uncommon to need it so it is passed as the last argument to be + * optional in the plugin. + */ + onInvite(InviteEvent{shared_from_this(), strify(orig), strify(params[1]), strify(params[0])}); +} + +void Server::handleJoin(const char *orig, const char **params) noexcept +{ + if (isSelf(strify(orig))) + m_jchannels.push_back(strify(params[0])); + + onJoin(JoinEvent{shared_from_this(), strify(orig), strify(params[0])}); +} + +void Server::handleKick(const char *orig, const char **params) noexcept +{ + if (isSelf(strify(params[1]))) { + // Remove the channel from the joined list. + removeJoinedChannel(strify(params[0])); + + // Rejoin the channel if the option has been set and I was kicked. + if (m_flags & AutoRejoin) + join(strify(params[0])); + } + + onKick(KickEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1]), strify(params[2])}); +} + +void Server::handleMode(const char *orig, const char **params) noexcept +{ + onMode(ModeEvent{shared_from_this(), strify(orig), strify(params[1])}); +} + +void Server::handleNick(const char *orig, const char **params) noexcept +{ + // Update our nickname. + if (isSelf(strify(orig))) + m_nickname = strify(params[0]); + + onNick(NickEvent{shared_from_this(), strify(orig), strify(params[0])}); +} + +void Server::handleNotice(const char *orig, const char **params) noexcept +{ + /* + * Like handleInvite, the notice provides the target nickname, we discard + * it. + */ + onNotice(NoticeEvent{shared_from_this(), strify(orig), strify(params[1])}); +} + +void Server::handleNumeric(unsigned int event, const char **params, unsigned int c) noexcept +{ + if (event == LIBIRC_RFC_RPL_NAMREPLY) { + /* + * Called multiple times to list clients on a channel. + * + * params[0] == originator + * params[1] == '=' + * params[2] == channel + * params[3] == list of users with their prefixes + * + * IDEA for the future: maybe give the appropriate mode as a second + * parameter in onNames. + */ + if (c < 4 || params[2] == nullptr || params[3] == nullptr) + return; + + std::vector<std::string> users = util::split(params[3], " \t"); + + // The listing may add some prefixes, remove them if needed. + for (std::string u : users) + m_namesMap[params[2]].insert(cleanPrefix(m_modes, u)); + } else if (event == LIBIRC_RFC_RPL_ENDOFNAMES) { + /* + * Called when end of name listing has finished on a channel. + * + * params[0] == originator + * params[1] == channel + * params[2] == End of NAMES list + */ + if (c < 3 || params[1] == nullptr) + return; + + auto it = m_namesMap.find(params[1]); + if (it != m_namesMap.end()) { + onNames(NamesEvent{shared_from_this(), params[1], std::vector<std::string>(it->second.begin(), it->second.end())}); + + // Don't forget to remove the list. + m_namesMap.erase(it); + } + } else if (event == LIBIRC_RFC_RPL_WHOISUSER) { + /* + * Called when whois information has been partially received. + * + * params[0] == originator + * params[1] == nickname + * params[2] == username + * params[3] == host + * params[4] == * (no idea what is that) + * params[5] == realname + */ + if (c < 6 || !params[1] || !params[2] || !params[3] || !params[5]) + return; + + Whois info; + + info.nick = strify(params[1]); + info.user = strify(params[2]); + info.host = strify(params[3]); + info.realname = strify(params[5]); + + m_whoisMap.emplace(info.nick, info); + } else if (event == LIBIRC_RFC_RPL_WHOISCHANNELS) { + /* + * Called when we have received channels for one user. + * + * params[0] == originator + * params[1] == nickname + * params[2] == list of channels with their prefixes + */ + if (c < 3 || !params[1] || !params[2]) + return; + + auto it = m_whoisMap.find(params[1]); + if (it != m_whoisMap.end()) { + std::vector<std::string> channels = util::split(params[2], " \t"); + + // Clean their prefixes. + for (auto &s : channels) + s = cleanPrefix(m_modes, s); + + it->second.channels = std::move(channels); + } + } else if (event == LIBIRC_RFC_RPL_ENDOFWHOIS) { + /* + * Called when whois is finished. + * + * params[0] == originator + * params[1] == nickname + * params[2] == End of WHOIS list + */ + auto it = m_whoisMap.find(params[1]); + if (it != m_whoisMap.end()) { + onWhois(WhoisEvent{shared_from_this(), it->second}); + + // Don't forget to remove. + m_whoisMap.erase(it); + } + } else if (event == /* RPL_BOUNCE */ 5) { + /* + * The event 5 is usually RPL_BOUNCE, but we always see it as ISUPPORT. + */ + for (unsigned int i = 0; i < c; ++i) { + if (strncmp(params[i], "PREFIX", 6) == 0) { + m_modes = extractPrefixes(params[i]); + break; + } + } + } +} + +void Server::handlePart(const char *orig, const char **params) noexcept +{ + // Remove the channel from the joined list if I left a channel. + if (isSelf(strify(orig))) + removeJoinedChannel(strify(params[0])); + + onPart(PartEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); +} + +void Server::handlePing(const char *, const char **params) noexcept +{ + // Reset the timer to detect disconnection. + m_timer.reset(); + + // Don't forget to respond. + send("PONG {}"_format(params[0])); +} + +void Server::handleQuery(const char *orig, const char **params) noexcept +{ + onQuery(QueryEvent{shared_from_this(), strify(orig), strify(params[1])}); +} + +void Server::handleTopic(const char *orig, const char **params) noexcept +{ + onTopic(TopicEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); +} + +std::shared_ptr<Server> Server::fromJson(const nlohmann::json &object) +{ + auto server = std::make_shared<Server>(util::json::requireIdentifier(object, "name")); + + server->setHost(util::json::requireString(object, "host")); + server->setPassword(util::json::getString(object, "password")); + server->setNickname(util::json::getString(object, "nickname", server->nickname())); + server->setRealname(util::json::getString(object, "realname", server->realname())); + server->setUsername(util::json::getString(object, "username", server->username())); + server->setCtcpVersion(util::json::getString(object, "ctcpVersion", server->ctcpVersion())); + server->setCommandCharacter(util::json::getString(object, "commandChar", server->commandCharacter())); + + if (object.find("port") != object.end()) + server->setPort(util::json::getUintRange<std::uint16_t>(object, "port")); + if (util::json::getBool(object, "ipv6")) + server->setFlags(server->flags() | Server::Ipv6); + if (util::json::getBool(object, "ssl")) + server->setFlags(server->flags() | Server::Ssl); + if (util::json::getBool(object, "sslVerify")) + server->setFlags(server->flags() | Server::SslVerify); + if (util::json::getBool(object, "autoRejoin")) + server->setFlags(server->flags() | Server::AutoRejoin); + if (util::json::getBool(object, "joinInvite")) + server->setFlags(server->flags() | Server::JoinInvite); + + return server; +} + +Channel Server::splitChannel(const std::string &value) +{ + auto pos = value.find(':'); + + if (pos != std::string::npos) + return { value.substr(0, pos), value.substr(pos + 1) }; + + return { value, "" }; +} + +Server::Server(std::string name) + : m_name(std::move(name)) + , m_session(std::make_unique<Session>()) + , m_state(std::make_unique<ConnectingState>()) +{ + irc_callbacks_t callbacks; + + /* + * GCC 4.9.2 triggers some missing-field-initializers warnings when + * using uniform initialization so use a std::memset as a workaround. + */ + std::memset(&callbacks, 0, sizeof (irc_callbacks_t)); + + /* + * Convert the raw pointer functions from libircclient to Server member + * function. + * + * While doing this, discard useless arguments. + */ + callbacks.event_channel = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleChannel(orig, params); + }; + callbacks.event_channel_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleChannelNotice(orig, params); + }; + callbacks.event_connect = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleConnect(orig, params); + }; + callbacks.event_ctcp_action = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleCtcpAction(orig, params); + }; + callbacks.event_invite = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleInvite(orig, params); + }; + callbacks.event_join = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleJoin(orig, params); + }; + callbacks.event_kick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleKick(orig, params); + }; + callbacks.event_mode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleChannelMode(orig, params); + }; + callbacks.event_nick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleNick(orig, params); + }; + callbacks.event_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleNotice(orig, params); + }; + callbacks.event_numeric = [] (irc_session_t *session, unsigned int event, const char *, const char **params, unsigned int count) { + static_cast<Server *>(irc_get_ctx(session))->handleNumeric(event, params, count); + }; + callbacks.event_part = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handlePart(orig, params); + }; + callbacks.event_ping = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handlePing(orig, params); + }; + callbacks.event_privmsg = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleQuery(orig, params); + }; + callbacks.event_topic = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleTopic(orig, params); + }; + callbacks.event_umode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) { + static_cast<Server *>(irc_get_ctx(session))->handleMode(orig, params); + }; + + m_session->m_handle = {irc_create_session(&callbacks), irc_destroy_session}; + + // Save this to the session. + irc_set_ctx(*m_session, this); + irc_set_ctcp_version(*m_session, m_ctcpversion.c_str()); +} + +Server::~Server() +{ + irc_disconnect(*m_session); +} + +void Server::setNickname(std::string nickname) +{ + if (m_session->isConnected()) + m_queue.push([=] () { + return irc_cmd_nick(*m_session, nickname.c_str()) == 0; + }); + else + m_nickname = std::move(nickname); +} + +void Server::setCtcpVersion(std::string ctcpversion) +{ + m_ctcpversion = std::move(ctcpversion); + irc_set_ctcp_version(*m_session, ctcpversion.c_str()); +} + +void Server::next(std::unique_ptr<State> state) noexcept +{ + m_stateNext = std::move(state); +} + +void Server::update() noexcept +{ + if (m_stateNext) { + log::debug("server {}: switch state {} -> {}"_format(m_name, m_state->ident(), m_stateNext->ident())); + + m_state = std::move(m_stateNext); + m_stateNext = nullptr; + + // Reset channels. + m_jchannels.clear(); + } +} + +void Server::disconnect() noexcept +{ + using namespace std::placeholders; + + irc_disconnect(*m_session); + onDie(); +} + +void Server::reconnect() noexcept +{ + irc_disconnect(*m_session); + next(std::make_unique<ConnectingState>()); +} + +void Server::prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept +{ + m_state->prepare(*this, setinput, setoutput, maxfd); +} + +void Server::sync(fd_set &setinput, fd_set &setoutput) +{ + /* + * 1. Send maximum of command possible if available for write + * + * Break on the first failure to avoid changing the order of the + * commands if any of them fails. + */ + bool done = false; + + while (!m_queue.empty() && !done) { + if (m_queue.front()()) + m_queue.pop(); + else + done = true; + } + + // 2. Read data. + irc_process_select_descriptors(*m_session, &setinput, &setoutput); +} + +bool Server::isSelf(const std::string &nick) const noexcept +{ + char target[32]{0}; + + irc_target_get_nick(nick.c_str(), target, sizeof (target)); + + return m_nickname == target; +} + +void Server::cmode(std::string channel, std::string mode) +{ + m_queue.push([=] () { + return irc_cmd_channel_mode(*m_session, channel.c_str(), mode.c_str()) == 0; + }); +} + +void Server::cnotice(std::string channel, std::string message) +{ + m_queue.push([=] () { + return irc_cmd_notice(*m_session, channel.c_str(), message.c_str()) == 0; + }); +} + +void Server::invite(std::string target, std::string channel) +{ + m_queue.push([=] () { + return irc_cmd_invite(*m_session, target.c_str(), channel.c_str()) == 0; + }); +} + +void Server::join(std::string channel, std::string password) +{ + // 1. Add the channel or update it to the requested channels. + auto it = std::find_if(m_rchannels.begin(), m_rchannels.end(), [&] (const auto &c) { + return c.name == channel; + }); + + if (it == m_rchannels.end()) + m_rchannels.push_back({ channel, password }); + else + *it = { channel, password }; + + // 2. Join if not found and connected. + if (m_session->isConnected()) + irc_cmd_join(*m_session, channel.c_str(), password.empty() ? nullptr : password.c_str()); +} + +void Server::kick(std::string target, std::string channel, std::string reason) +{ + m_queue.push([=] () { + return irc_cmd_kick(*m_session, target.c_str(), channel.c_str(), reason.c_str()) == 0; + }); +} + +void Server::me(std::string target, std::string message) +{ + m_queue.push([=] () { + return irc_cmd_me(*m_session, target.c_str(), message.c_str()) == 0; + }); +} + +void Server::message(std::string target, std::string message) +{ + m_queue.push([=] () { + return irc_cmd_msg(*m_session, target.c_str(), message.c_str()) == 0; + }); +} + +void Server::mode(std::string mode) +{ + m_queue.push([=] () { + return irc_cmd_user_mode(*m_session, mode.c_str()) == 0; + }); +} + +void Server::names(std::string channel) +{ + m_queue.push([=] () { + return irc_cmd_names(*m_session, channel.c_str()) == 0; + }); +} + +void Server::notice(std::string target, std::string message) +{ + m_queue.push([=] () { + return irc_cmd_notice(*m_session, target.c_str(), message.c_str()) == 0; + }); +} + +void Server::part(std::string channel, std::string reason) +{ + m_queue.push([=] () -> bool { + if (reason.empty()) + return irc_cmd_part(*m_session, channel.c_str()) == 0; + + return irc_send_raw(*m_session, "PART %s :%s", channel.c_str(), reason.c_str()); + }); +} + +void Server::send(std::string raw) +{ + m_queue.push([=] () { + return irc_send_raw(*m_session, raw.c_str()) == 0; + }); +} + +void Server::topic(std::string channel, std::string topic) +{ + m_queue.push([=] () { + return irc_cmd_topic(*m_session, channel.c_str(), topic.c_str()) == 0; + }); +} + +void Server::whois(std::string target) +{ + m_queue.push([=] () { + return irc_cmd_whois(*m_session, target.c_str()) == 0; + }); +} + +/* + * Server::DisconnectedState implementation + * ------------------------------------------------------------------ + */ + +void Server::DisconnectedState::prepare(Server &server, fd_set &, fd_set &, net::Handle &) +{ + if (server.m_recotries == 0) { + log::warning() << "server " << server.m_name << ": reconnection disabled, skipping" << std::endl; + server.onDie(); + } else if (server.m_recotries > 0 && server.m_recocur > server.m_recotries) { + log::warning() << "server " << server.m_name << ": giving up" << std::endl; + server.onDie(); + } else { + if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) { + irc_disconnect(*server.m_session); + + server.m_recocur ++; + server.next(std::make_unique<ConnectingState>()); + } + } +} + +std::string Server::DisconnectedState::ident() const +{ + return "Disconnected"; +} + +/* + * Server::ConnectingState implementation + * ------------------------------------------------------------------ + */ + +bool Server::ConnectingState::connect(Server &server) +{ + const char *password = server.m_password.empty() ? nullptr : server.m_password.c_str(); + std::string host = server.m_host; + int code; + + // libircclient requires # for SSL connection. +#if defined(WITH_SSL) + if (server.m_flags & Server::Ssl) + host.insert(0, 1, '#'); + if (!(server.m_flags & Server::SslVerify)) + irc_option_set(*server.m_session, LIBIRC_OPTION_SSL_NO_VERIFY); +#endif + + if (server.flags() & Server::Ipv6) { + code = irc_connect6(*server.m_session, host.c_str(), server.m_port, password, + server.m_nickname.c_str(), + server.m_username.c_str(), + server.m_realname.c_str()); + } else { + code = irc_connect(*server.m_session, host.c_str(), server.m_port, password, + server.m_nickname.c_str(), + server.m_username.c_str(), + server.m_realname.c_str()); + } + + return code == 0; +} + +void Server::ConnectingState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) +{ + /* + * The connect function will either fail if the hostname wasn't resolved or + * if any of the internal functions fail. + * + * It returns success if the connection was successful but it does not mean + * that connection is established. + * + * Because this function will be called repeatidly, the connection was + * started and we're still not connected in the specified timeout time, we + * mark the server as disconnected. + * + * Otherwise, the libircclient event_connect will change the state. + */ + if (m_state == Connecting) { + if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) { + log::warning() << "server " << server.name() << ": timeout while connecting" << std::endl; + server.next(std::make_unique<DisconnectedState>()); + } else if (!irc_is_connected(*server.m_session)) { + log::warning() << "server " << server.m_name << ": error while connecting: "; + log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl; + + if (server.m_recotries != 0) + log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay)); + + server.next(std::make_unique<DisconnectedState>()); + } else + irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd)); + } else { + /* + * This is needed if irccd is started before DHCP or if DNS cache is + * outdated. + */ +#if !defined(IRCCD_SYSTEM_WINDOWS) + (void)res_init(); +#endif + log::info("server {}: trying to connect to {}, port {}"_format(server.m_name, server.m_host, server.m_port)); + + if (!connect(server)) { + log::warning() << "server " << server.m_name << ": disconnected while connecting: "; + log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl; + server.next(std::make_unique<DisconnectedState>()); + } else { + m_state = Connecting; + + if (irc_is_connected(*server.m_session)) + irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd)); + } + } +} + +std::string Server::ConnectingState::ident() const +{ + return "Connecting"; +} + +/* + * Server::ConnectedState implementation + * ------------------------------------------------------------------ + */ + +void Server::ConnectedState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) +{ + if (!irc_is_connected(*server.m_session)) { + log::warning() << "server " << server.m_name << ": disconnected" << std::endl; + + if (server.m_recodelay > 0) + log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay)); + + server.next(std::make_unique<DisconnectedState>()); + } else if (server.m_timer.elapsed() >= server.m_timeout * 1000) { + log::warning() << "server " << server.m_name << ": ping timeout after " + << (server.m_timer.elapsed() / 1000) << " seconds" << std::endl; + server.next(std::make_unique<DisconnectedState>()); + } else + irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd)); +} + +std::string Server::ConnectedState::ident() const +{ + return "Connected"; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/server.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,957 @@ +/* + * server.hpp -- an IRC server + * + * Copyright (c) 2013-2016 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_SERVER_HPP +#define IRCCD_SERVER_HPP + +/** + * \file server.hpp + * \brief IRC Server. + */ + +#include <cstdint> +#include <functional> +#include <map> +#include <memory> +#include <queue> +#include <set> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <json.hpp> + +#include "elapsed-timer.hpp" +#include "net.hpp" +#include "signals.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +class Server; + +/** + * \brief Prefixes for nicknames. + */ +enum class ChannelMode { + Creator = 'O', //!< Channel creator + HalfOperator = 'h', //!< Half operator + Operator = 'o', //!< Channel operator + Protection = 'a', //!< Unkillable + Voiced = 'v' //!< Voice power +}; + +/** + * \brief A channel to join with an optional password. + */ +class Channel { +public: + std::string name; //!< the channel to join + std::string password; //!< the optional password +}; + +/** + * \brief Describe a whois information. + */ +class Whois { +public: + std::string nick; //!< user's nickname + std::string user; //!< user's user + std::string host; //!< hostname + std::string realname; //!< realname + std::vector<std::string> channels; //!< the channels where the user is +}; + +/** + * \brief Channel event. + */ +class ChannelModeEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string mode; //!< The mode. + std::string argument; //!< The mode argument (Optional). +}; + +/** + * \brief Channel notice event. + */ +class ChannelNoticeEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string message; //!< The notice message. +}; + +/** + * \brief Connection success event. + */ +class ConnectEvent { +public: + std::shared_ptr<Server> server; //!< The server. +}; + +/** + * \brief Invite event. + */ +class InviteEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string nickname; //!< The nickname (you). +}; + +/** + * \brief Join event. + */ +class JoinEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. +}; + +/** + * \brief Kick event. + */ +class KickEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string target; //!< The target. + std::string reason; //!< The reason (Optional). +}; + +/** + * \brief Message event. + */ +class MessageEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string message; //!< The message. +}; + +/** + * \brief CTCP action event. + */ +class MeEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string message; //!< The message. +}; + +/** + * \brief Mode event. + */ +class ModeEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string mode; //!< The mode. +}; + +/** + * \brief Names listing event. + */ +class NamesEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string channel; //!< The channel. + std::vector<std::string> names; //!< The names. +}; + +/** + * \brief Nick change event. + */ +class NickEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string nickname; //!< The new nickname. +}; + +/** + * \brief Notice event. + */ +class NoticeEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string message; //!< The message. +}; + +/** + * \brief Part event. + */ +class PartEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string reason; //!< The reason. +}; + +/** + * \brief Query event. + */ +class QueryEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string message; //!< The message. +}; + +/** + * \brief Topic event. + */ +class TopicEvent { +public: + std::shared_ptr<Server> server; //!< The server. + std::string origin; //!< The originator. + std::string channel; //!< The channel. + std::string topic; //!< The topic message. +}; + +/** + * \brief Whois event. + */ +class WhoisEvent { +public: + std::shared_ptr<Server> server; //!< The server. + Whois whois; //!< The whois information. +}; + +/** + * \brief The class that connect to a IRC server + * + * The server is a class that stores callbacks which will be called on IRC + * events. It is the lowest part of the connection to a server, it can be used + * directly by the user to connect to a server. + * + * The server has several signals that will be emitted when data has arrived. + * + * When adding a server to the ServerService in irccd, these signals are + * connected to generate events that will be dispatched to the plugins and to + * the transports. + * + * Note: the server is set in non blocking mode, commands are placed in a queue + * and sent when only when they are ready. + */ +class Server : public std::enable_shared_from_this<Server> { +public: + /** + * Bridge for libircclient. + */ + class Session; + + /** + * \brief Various options for server. + */ + enum { + Ipv6 = (1 << 0), //!< Connect using IPv6 + Ssl = (1 << 1), //!< Use SSL + SslVerify = (1 << 2), //!< Verify SSL + AutoRejoin = (1 << 3), //!< Auto rejoin a kick + JoinInvite = (1 << 4) //!< Join a channel on invitation + }; + + /** + * Signal: onChannelMode + * ---------------------------------------------------------- + * + * Triggered when someone changed the channel mode. + */ + Signal<ChannelModeEvent> onChannelMode; + + /** + * Signal: onChannelNotice + * ---------------------------------------------------------- + * + * Triggered when a notice has been sent on a channel. + */ + Signal<ChannelNoticeEvent> onChannelNotice; + + /** + * Signal: onConnect + * ---------------------------------------------------------- + * + * Triggered when the server is successfully connected. + */ + Signal<ConnectEvent> onConnect; + + /** + * Signal: onDie + * ---------------------------------------------------------- + * + * The server is dead. + */ + Signal<> onDie; + + /** + * Signal: onInvite + * ---------------------------------------------------------- + * + * Triggered when an invite has been sent to you (the bot). + */ + Signal<InviteEvent> onInvite; + + /** + * Signal: onJoin + * ---------------------------------------------------------- + * + * Triggered when a user has joined the channel, it also includes you. + */ + Signal<JoinEvent> onJoin; + + /** + * Signal: onKick + * ---------------------------------------------------------- + * + * Triggered when someone has been kicked from a channel. + */ + Signal<KickEvent> onKick; + + /** + * ServerEvent: onMessage + * ---------------------------------------------------------- + * + * Triggered when a message on a channel has been sent. + */ + Signal<MessageEvent> onMessage; + + /** + * Signal: onMe + * ---------------------------------------------------------- + * + * Triggered on a CTCP Action. + * + * This is both used in a channel and in a private message so the target may + * be a channel or your nickname. + */ + Signal<MeEvent> onMe; + + /** + * Signal: onMode + * ---------------------------------------------------------- + * + * Triggered when the server changed your user mode. + */ + Signal<ModeEvent> onMode; + + /** + * Signal: onNames + * ---------------------------------------------------------- + * + * Triggered when names listing has finished on a channel. + */ + Signal<NamesEvent> onNames; + + /** + * Signal: onNick + * ---------------------------------------------------------- + * + * Triggered when someone changed its nickname, it also includes you. + */ + Signal<NickEvent> onNick; + + /** + * Signal: onNotice + * ---------------------------------------------------------- + * + * Triggered when someone has sent a notice to you. + */ + Signal<NoticeEvent> onNotice; + + /** + * Signal: onPart + * ---------------------------------------------------------- + * + * Triggered when someone has left the channel. + */ + Signal<PartEvent> onPart; + + /** + * Signal: onQuery + * ---------------------------------------------------------- + * + * Triggered when someone has sent you a private message. + */ + Signal<QueryEvent> onQuery; + + /** + * Signal: onTopic + * ---------------------------------------------------------- + * + * Triggered when someone changed the channel topic. + */ + Signal<TopicEvent> onTopic; + + /** + * Signal: onWhois + * ---------------------------------------------------------- + * + * Triggered when whois information has been received. + */ + Signal<WhoisEvent> onWhois; + +private: + class State; + class ConnectedState; + class ConnectingState; + class DisconnectedState; + + // Requested and joined channels. + std::vector<Channel> m_rchannels; + std::vector<std::string> m_jchannels; + + // Identifier. + std::string m_name; + + // Connection information + std::string m_host; + std::string m_password; + std::uint16_t m_port{6667}; + std::uint8_t m_flags{0}; + + // Identity. + std::string m_nickname{"irccd"}; + std::string m_username{"irccd"}; + std::string m_realname{"IRC Client Daemon"}; + std::string m_ctcpversion{"IRC Client Daemon"}; + + // Settings. + std::string m_commandCharacter{"!"}; + std::int8_t m_recotries{-1}; + std::uint16_t m_recodelay{30}; + std::uint16_t m_timeout{300}; + + // Queue of requests to send. + std::queue<std::function<bool ()>> m_queue; + + // libircclient session (bridge). + std::unique_ptr<Session> m_session; + + // States. + std::unique_ptr<State> m_state; + std::unique_ptr<State> m_stateNext; + + // Misc. + ElapsedTimer m_timer; + std::map<ChannelMode, char> m_modes; + std::int8_t m_recocur{0}; + std::map<std::string, std::set<std::string>> m_namesMap; + std::map<std::string, Whois> m_whoisMap; + + // Private helpers. + void removeJoinedChannel(const std::string &channel); + + // Handle libircclient callbacks. + void handleChannel(const char *, const char **) noexcept; + void handleChannelMode(const char *, const char **) noexcept; + void handleChannelNotice(const char *, const char **) noexcept; + void handleConnect(const char *, const char **) noexcept; + void handleCtcpAction(const char *, const char **) noexcept; + void handleInvite(const char *, const char **) noexcept; + void handleJoin(const char *, const char **) noexcept; + void handleKick(const char *, const char **) noexcept; + void handleMode(const char *, const char **) noexcept; + void handleNick(const char *, const char **) noexcept; + void handleNotice(const char *, const char **) noexcept; + void handleNumeric(unsigned int, const char **, unsigned int) noexcept; + void handlePart(const char *, const char **) noexcept; + void handlePing(const char *, const char **) noexcept; + void handleQuery(const char *, const char **) noexcept; + void handleTopic(const char *, const char **) noexcept; + +public: + /** + * Convert a JSON object as a server. + * + * Used in JavaScript API and transport commands. + * + * \param object the object + * \return the server + * \throw std::exception on failures + */ + IRCCD_EXPORT static std::shared_ptr<Server> fromJson(const nlohmann::json &object); + + /** + * Split a channel from the form channel:password into a ServerChannel + * object. + * + * \param value the value + * \return a channel + */ + IRCCD_EXPORT static Channel splitChannel(const std::string &value); + + /** + * Construct a server. + * + * \param name the identifier + */ + IRCCD_EXPORT Server(std::string name); + + /** + * Destructor. Close the connection if needed. + */ + IRCCD_EXPORT virtual ~Server(); + + /** + * Get the server identifier. + * + * \return the id + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Get the hostname. + * + * \return the hostname + */ + inline const std::string &host() const noexcept + { + return m_host; + } + + /** + * Set the hostname. + * + * \param host the hostname + */ + inline void setHost(std::string host) noexcept + { + m_host = std::move(host); + } + + /** + * Get the password. + * + * \return the password + */ + inline const std::string &password() const noexcept + { + return m_password; + } + + /** + * Set the password. + * + * An empty password means no password. + * + * \param password the password + */ + inline void setPassword(std::string password) noexcept + { + m_password = std::move(password); + } + + /** + * Get the port. + * + * \return the port + */ + inline std::uint16_t port() const noexcept + { + return m_port; + } + + /** + * Set the port. + * + * \param port the port + */ + inline void setPort(std::uint16_t port) noexcept + { + m_port = port; + } + + /** + * Get the flags. + * + * \return the flags + */ + inline std::uint8_t flags() const noexcept + { + return m_flags; + } + + /** + * Set the flags. + * + * \pre flags must be valid + * \param flags the flags + */ + inline void setFlags(std::uint8_t flags) noexcept + { + assert(flags <= 0x1f); + + m_flags = flags; + } + + /** + * Get the nickname. + * + * \return the nickname + */ + inline const std::string &nickname() const noexcept + { + return m_nickname; + } + + /** + * Set the nickname. + * + * If the server is connected, send a nickname command to the IRC server, + * otherwise change it locally. + * + * \param nickname the nickname + */ + IRCCD_EXPORT void setNickname(std::string nickname); + + /** + * Get the username. + * + * \return the username + */ + inline const std::string &username() const noexcept + { + return m_username; + } + + /** + * Set the username. + * + * \param name the username + * \note the username will be changed on the next connection + */ + inline void setUsername(std::string name) noexcept + { + m_username = std::move(name); + } + + /** + * Get the realname. + * + * \return the realname + */ + inline const std::string &realname() const noexcept + { + return m_realname; + } + + /** + * Set the realname. + * + * \param realname the username + * \note the username will be changed on the next connection + */ + inline void setRealname(std::string realname) noexcept + { + m_realname = std::move(realname); + } + + /** + * Get the CTCP version. + * + * \return the CTCP version + */ + inline const std::string &ctcpVersion() const noexcept + { + return m_ctcpversion; + } + + /** + * Set the CTCP version. + * + * \param ctcpversion the version + */ + IRCCD_EXPORT void setCtcpVersion(std::string ctcpversion); + + /** + * Get the command character. + * + * \return the character + */ + inline const std::string &commandCharacter() const noexcept + { + return m_commandCharacter; + } + + /** + * Set the command character. + * + * \pre !commandCharacter.empty() + * \param commandCharacter the command character + */ + inline void setCommandCharacter(std::string commandCharacter) noexcept + { + assert(!commandCharacter.empty()); + + m_commandCharacter = std::move(commandCharacter); + } + + /** + * Get the number of reconnections before giving up. + * + * \return the number of reconnections + */ + inline std::int8_t reconnectTries() const noexcept + { + return m_recotries; + } + + /** + * Set the number of reconnections to test before giving up. + * + * A value less than 0 means infinite. + * + * \param reconnectTries the number of reconnections + */ + inline void setReconnectTries(std::int8_t reconnectTries) noexcept + { + m_recotries = reconnectTries; + } + + /** + * Get the reconnection delay before retrying. + * + * \return the number of seconds + */ + inline std::uint16_t reconnectDelay() const noexcept + { + return m_recodelay; + } + + /** + * Set the number of seconds before retrying. + * + * \param reconnectDelay the number of seconds + */ + inline void setReconnectDelay(std::uint16_t reconnectDelay) noexcept + { + m_recodelay = reconnectDelay; + } + + /** + * Get the ping timeout. + * + * \return the ping timeout + */ + inline std::uint16_t pingTimeout() const noexcept + { + return m_timeout; + } + + /** + * Set the ping timeout before considering a server as dead. + * + * \param pingTimeout the delay in seconds + */ + inline void setPingTimeout(std::uint16_t pingTimeout) noexcept + { + m_timeout = pingTimeout; + } + + /** + * Get the list of channels joined. + * + * \return the channels + */ + inline const std::vector<std::string> &channels() const noexcept + { + return m_jchannels; + } + + /** + * Set the next state, it is not changed immediately but on next iteration. + * + * \param state the new state + */ + IRCCD_EXPORT void next(std::unique_ptr<State> state) noexcept; + + /** + * Switch to next state if it has. + */ + IRCCD_EXPORT void update() noexcept; + + /** + * Force disconnection. + */ + IRCCD_EXPORT void disconnect() noexcept; + + /** + * Asks for a reconnection. + */ + IRCCD_EXPORT void reconnect() noexcept; + + /** + * Prepare the IRC session. + * + * \warning Not thread-safe + */ + IRCCD_EXPORT void prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept; + + /** + * Process incoming/outgoing data after selection. + * + * \param setinput + * \param setoutput + * \throw any exception that have been throw from user functions + */ + IRCCD_EXPORT void sync(fd_set &setinput, fd_set &setoutput); + + /** + * Determine if the nickname is the bot itself. + * + * \param nick the nickname to check + * \return true if it is the bot + */ + IRCCD_EXPORT bool isSelf(const std::string &nick) const noexcept; + + /** + * Change the channel mode. + * + * \param channel the channel + * \param mode the new mode + */ + IRCCD_EXPORT virtual void cmode(std::string channel, std::string mode); + + /** + * Send a channel notice. + * + * \param channel the channel + * \param message message notice + */ + IRCCD_EXPORT virtual void cnotice(std::string channel, std::string message); + + /** + * Invite a user to a channel. + * + * \param target the target nickname + * \param channel the channel + */ + IRCCD_EXPORT virtual void invite(std::string target, std::string channel); + + /** + * Join a channel, the password is optional and can be kept empty. + * + * \param channel the channel to join + * \param password the optional password + */ + IRCCD_EXPORT virtual void join(std::string channel, std::string password = ""); + + /** + * Kick someone from the channel. Please be sure to have the rights + * on that channel because errors won't be reported. + * + * \param target the target to kick + * \param channel from which channel + * \param reason the optional reason + */ + IRCCD_EXPORT virtual void kick(std::string target, std::string channel, std::string reason = ""); + + /** + * Send a CTCP Action as known as /me. The target may be either a + * channel or a nickname. + * + * \param target the nickname or the channel + * \param message the message + */ + IRCCD_EXPORT virtual void me(std::string target, std::string message); + + /** + * Send a message to the specified target or channel. + * + * \param target the target + * \param message the message + */ + IRCCD_EXPORT virtual void message(std::string target, std::string message); + + /** + * Change your user mode. + * + * \param mode the mode + */ + IRCCD_EXPORT virtual void mode(std::string mode); + + /** + * Request the list of names. + * + * \param channel the channel + */ + IRCCD_EXPORT virtual void names(std::string channel); + + /** + * Send a private notice. + * + * \param target the target + * \param message the notice message + */ + IRCCD_EXPORT virtual void notice(std::string target, std::string message); + + /** + * Part from a channel. + * + * Please note that the reason is not supported on all servers so if you + * want portability, don't provide it. + * + * \param channel the channel to leave + * \param reason the optional reason + */ + IRCCD_EXPORT virtual void part(std::string channel, std::string reason = ""); + + /** + * Send a raw message to the IRC server. You don't need to add + * message terminators. + * + * \warning Use this function with care + * \param raw the raw message (without `\r\n\r\n`) + */ + IRCCD_EXPORT virtual void send(std::string raw); + + /** + * Change the channel topic. + * + * \param channel the channel + * \param topic the desired topic + */ + IRCCD_EXPORT virtual void topic(std::string channel, std::string topic); + + /** + * Request for whois information. + * + * \param target the target nickname + */ + IRCCD_EXPORT virtual void whois(std::string target); +}; + +} // !irccd + +#endif // !IRCCD_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-command.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,39 @@ +/* + * service-command.cpp -- store remote commands + * + * Copyright (c) 2013-2016 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 <algorithm> + +#include "service-command.hpp" + +namespace irccd { + +bool CommandService::contains(const std::string &name) const noexcept +{ + return find(name) != nullptr; +} + +std::shared_ptr<Command> CommandService::find(const std::string &name) const noexcept +{ + auto it = std::find_if(m_commands.begin(), m_commands.end(), [&] (const auto &cmd) { + return cmd->name() == name; + }); + + return it == m_commands.end() ? nullptr : *it; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-command.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,73 @@ +/* + * service-command.hpp -- store remote commands + * + * Copyright (c) 2013-2016 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_SERVICE_COMMAND_HPP +#define IRCCD_SERVICE_COMMAND_HPP + +/** + * \file service-command.hpp + * \brief Store remote commands. + */ + +#include <memory> +#include <vector> + +#include "command.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +/** + * \brief Store remote commands. + * \ingroup services + */ +class CommandService { +private: + std::vector<std::shared_ptr<Command>> m_commands; + +public: + /** + * Get all commands. + * + * \return the list of commands. + */ + inline const std::vector<std::shared_ptr<Command>> &commands() const noexcept + { + return m_commands; + } + + /** + * Tells if a command exists. + * + * \param name the command name + * \return true if the command exists + */ + IRCCD_EXPORT bool contains(const std::string &name) const noexcept; + + /** + * Find a command by name. + * + * \param name the command name + * \return the command or empty one if not found + */ + IRCCD_EXPORT std::shared_ptr<Command> find(const std::string &name) const noexcept; +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_COMMAND_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-interrupt.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,75 @@ +/* + * service-interrupt.cpp -- interrupt irccd event loop + * + * Copyright (c) 2013-2016 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 <array> + +#include "logger.hpp" +#include "service-interrupt.hpp" + +namespace irccd { + +InterruptService::InterruptService() + : m_in(AF_INET, 0) + , m_out(AF_INET, 0) +{ + // Bind a socket to any port. + m_in.set(net::option::SockReuseAddress(true)); + m_in.bind(net::ipv4::any(0)); + m_in.listen(1); + + // Do the socket pair. + m_out.connect(net::ipv4::pton("127.0.0.1", net::ipv4::port(m_in.getsockname()))); + m_in = m_in.accept(); + m_out.set(net::option::SockBlockMode(false)); +} + +void InterruptService::prepare(fd_set &in, fd_set &, net::Handle &max) +{ + FD_SET(m_in.handle(), &in); + + if (m_in.handle() > max) + max = m_in.handle(); +} + +void InterruptService::sync(fd_set &in, fd_set &) +{ + if (FD_ISSET(m_in.handle(), &in)) { + static std::array<char, 32> tmp; + + try { + log::debug("irccd: interrupt service recv"); + m_in.recv(tmp.data(), 32); + } catch (const std::exception &ex) { + log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl; + } + } +} + +void InterruptService::interrupt() noexcept +{ + try { + static char byte; + + log::debug("irccd: interrupt service send"); + m_out.send(&byte, 1); + } catch (const std::exception &ex) { + log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl; + } +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-interrupt.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,66 @@ +/* + * service-interrupt.hpp -- interrupt irccd event loop + * + * Copyright (c) 2013-2016 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_SERVICE_INTERRUPT_HPP +#define IRCCD_SERVICE_INTERRUPT_HPP + +/** + * \file service-interrupt.hpp + * \brief Interrupt irccd event loop. + */ + +#include "net.hpp" + +namespace irccd { + +/** + * \brief Interrupt irccd event loop. + * \ingroup services + */ +class InterruptService { +private: + net::TcpSocket m_in; + net::TcpSocket m_out; + +public: + /** + * Prepare the socket pair. + * + * \throw std::runtime_error on errors + */ + IRCCD_EXPORT InterruptService(); + + /** + * \copydoc Service::prepare + */ + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc Service::sync + */ + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); + + /** + * Request interruption. + */ + IRCCD_EXPORT void interrupt() noexcept; +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_INTERRUPT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-plugin.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,178 @@ +/* + * service-plugin.cpp -- manage plugins + * + * Copyright (c) 2013-2016 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 <algorithm> +#include <functional> +#include <regex> +#include <stdexcept> + +#include <format.h> + +#include "logger.hpp" +#include "service-plugin.hpp" + +using namespace fmt::literals; + +namespace irccd { + +PluginService::PluginService(Irccd &irccd) noexcept + : m_irccd(irccd) +{ +} + +PluginService::~PluginService() +{ + for (const auto &plugin : m_plugins) + plugin->onUnload(m_irccd); +} + +bool PluginService::has(const std::string &name) const noexcept +{ + return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) { + return plugin->name() == name; + }) > 0; +} + +std::shared_ptr<Plugin> PluginService::get(const std::string &name) const noexcept +{ + auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { + return plugin->name() == name; + }); + + if (it == m_plugins.end()) + return nullptr; + + return *it; +} + +std::shared_ptr<Plugin> PluginService::require(const std::string &name) const +{ + auto plugin = get(name); + + if (!plugin) + throw std::invalid_argument("plugin {} not found"_format(name)); + + return plugin; +} + +void PluginService::add(std::shared_ptr<Plugin> plugin) +{ + m_plugins.push_back(std::move(plugin)); +} + +void PluginService::setConfig(const std::string &name, PluginConfig config) +{ + m_config.emplace(name, std::move(config)); +} + +PluginConfig PluginService::config(const std::string &name) const +{ + auto it = m_config.find(name); + + if (it != m_config.end()) + return it->second; + + return PluginConfig(); +} + +void PluginService::setFormats(const std::string &name, PluginFormats formats) +{ + m_formats.emplace(name, std::move(formats)); +} + +PluginFormats PluginService::formats(const std::string &name) const +{ + auto it = m_formats.find(name); + + if (it != m_formats.end()) + return it->second; + + return PluginFormats(); +} + +std::shared_ptr<Plugin> PluginService::open(const std::string &id, + const std::string &path) +{ + for (const auto &loader : m_loaders) { + auto plugin = loader->open(id, path); + + if (plugin) + return plugin; + } + + return nullptr; +} + +std::shared_ptr<Plugin> PluginService::find(const std::string &id) +{ + for (const auto &loader : m_loaders) { + auto plugin = loader->find(id); + + if (plugin) + return plugin; + } + + return nullptr; +} + +void PluginService::load(std::string name, std::string path) +{ + if (has(name)) + return; + + try { + std::shared_ptr<Plugin> plugin; + + if (path.empty()) + plugin = find(name); + else + plugin = open(name, std::move(path)); + + if (plugin) { + plugin->setConfig(m_config[name]); + plugin->setFormats(m_formats[name]); + plugin->onLoad(m_irccd); + + add(std::move(plugin)); + } + } catch (const std::exception &ex) { + log::warning("plugin {}: {}"_format(name, ex.what())); + } +} + +void PluginService::reload(const std::string &name) +{ + auto plugin = get(name); + + if (plugin) + plugin->onReload(m_irccd); +} + +void PluginService::unload(const std::string &name) +{ + auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { + return plugin->name() == name; + }); + + if (it != m_plugins.end()) { + (*it)->onUnload(m_irccd); + m_plugins.erase(it); + } +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-plugin.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,190 @@ +/* + * service-plugin.hpp -- manage plugins + * + * Copyright (c) 2013-2016 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_SERVICE_PLUGIN_HPP +#define IRCCD_SERVICE_PLUGIN_HPP + +/** + * \file service-plugin.hpp + * \brief Manage plugins. + */ + +#include <unordered_map> +#include <memory> +#include <vector> + +#include "plugin.hpp" + +namespace irccd { + +class Irccd; + +/** + * \brief Manage plugins. + * \ingroup services + */ +class PluginService { +private: + Irccd &m_irccd; + std::vector<std::shared_ptr<Plugin>> m_plugins; + std::vector<std::unique_ptr<PluginLoader>> m_loaders; + std::unordered_map<std::string, PluginConfig> m_config; + std::unordered_map<std::string, PluginFormats> m_formats; + +public: + /** + * Create the plugin service. + * + * \param irccd the irccd instance + */ + IRCCD_EXPORT PluginService(Irccd &irccd) noexcept; + + /** + * Destroy plugins. + */ + IRCCD_EXPORT ~PluginService(); + + /** + * Get the list of plugins. + * + * \return the list of plugins + */ + inline const std::vector<std::shared_ptr<Plugin>> &list() const noexcept + { + return m_plugins; + } + + /** + * Check if a plugin is loaded. + * + * \param name the plugin id + * \return true if has plugin + */ + IRCCD_EXPORT bool has(const std::string &name) const noexcept; + + /** + * Get a loaded plugin or null if not found. + * + * \param name the plugin id + * \return the plugin or empty one if not found + */ + IRCCD_EXPORT std::shared_ptr<Plugin> get(const std::string &name) const noexcept; + + /** + * Find a loaded plugin. + * + * \param name the plugin id + * \return the plugin + * \throws std::out_of_range if not found + */ + IRCCD_EXPORT std::shared_ptr<Plugin> require(const std::string &name) const; + + /** + * Add the specified plugin to the registry. + * + * \pre plugin != nullptr + * \param plugin the plugin + * \note the plugin is only added to the list, no action is performed on it + */ + IRCCD_EXPORT void add(std::shared_ptr<Plugin> plugin); + + /** + * Configure a plugin. + * + * If the plugin is already loaded, its configuration is updated. + * + * \param name the plugin name + * \param config the new configuration + */ + IRCCD_EXPORT void setConfig(const std::string &name, PluginConfig config); + + /** + * Get a configuration for a plugin. + * + * \param name the plugin name + * \return the configuration or default one if not found + */ + IRCCD_EXPORT PluginConfig config(const std::string &name) const; + + /** + * Add formatting for a plugin. + * + * \param name the plugin name + * \param formats the formats + */ + IRCCD_EXPORT void setFormats(const std::string &name, PluginFormats formats); + + /** + * Get formats for a plugin. + * + * \param name the plugin name + * \return the formats + */ + IRCCD_EXPORT PluginFormats formats(const std::string &name) const; + + /** + * Generic function for opening the plugin at the given path. + * + * This function will search for every PluginLoader and call open() on it, + * the first one that success will be returned. + * + * \param id the plugin id + * \param path the path to the file + * \return the plugin or nullptr on failures + */ + IRCCD_EXPORT std::shared_ptr<Plugin> open(const std::string &id, + const std::string &path); + + /** + * Generic function for finding a plugin. + * + * \param id the plugin id + * \return the plugin or nullptr on failures + */ + IRCCD_EXPORT std::shared_ptr<Plugin> find(const std::string &id); + + /** + * Convenient wrapper that loads a plugin, call onLoad and add it to the + * registry. + * + * Any errors are printed using logger. + * + * \param name the name + * \param path the optional path (searched if empty) + */ + IRCCD_EXPORT void load(std::string name, std::string path = ""); + + /** + * Unload a plugin and remove it. + * + * \param name the plugin id + */ + IRCCD_EXPORT void unload(const std::string &name); + + /** + * Reload a plugin by calling onReload. + * + * \param name the plugin name + * \throw std::exception on failures + */ + IRCCD_EXPORT void reload(const std::string &name); +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_PLUGIN_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-rule.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,78 @@ +/* + * service-rule.cpp -- store and solve rules + * + * Copyright (c) 2013-2016 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 <cassert> + +#include <format.h> + +#include "logger.hpp" +#include "service-rule.hpp" +#include "util.hpp" + +using namespace fmt::literals; + +namespace irccd { + +void RuleService::add(Rule rule) +{ + m_rules.push_back(std::move(rule)); +} + +void RuleService::insert(Rule rule, unsigned position) +{ + assert(position <= m_rules.size()); + + m_rules.insert(m_rules.begin() + position, std::move(rule)); +} + +void RuleService::remove(unsigned position) +{ + assert(position < m_rules.size()); + + m_rules.erase(m_rules.begin() + position); +} + +bool RuleService::solve(const std::string &server, + const std::string &channel, + const std::string &origin, + const std::string &plugin, + const std::string &event) noexcept +{ + bool result = true; + + log::debug("rule: solving for server={}, channel={}, origin={}, plugin={}, event={}"_format(server, channel, + origin, plugin, event)); + + int i = 0; + for (const Rule &rule : m_rules) { + log::debug() << " candidate " << i++ << ":\n" + << " servers: " << util::join(rule.servers().begin(), rule.servers().end()) << "\n" + << " channels: " << util::join(rule.channels().begin(), rule.channels().end()) << "\n" + << " origins: " << util::join(rule.origins().begin(), rule.origins().end()) << "\n" + << " plugins: " << util::join(rule.plugins().begin(), rule.plugins().end()) << "\n" + << " events: " << util::join(rule.events().begin(), rule.events().end()) << "\n" + << " action: " << ((rule.action() == RuleAction::Accept) ? "accept" : "drop") << std::endl; + + if (rule.match(server, channel, origin, plugin, event)) + result = rule.action() == RuleAction::Accept; + } + + return result; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-rule.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,104 @@ +/* + * service-rule.hpp -- store and solve rules + * + * Copyright (c) 2013-2016 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_SERVICE_RULE_HPP +#define IRCCD_SERVICE_RULE_HPP + +/** + * \file service-rule.hpp + * \brief Store and solve rules. + */ + +#include <vector> + +#include "rule.hpp" + +namespace irccd { + +/** + * \brief Store and solve rules. + * \ingroup services + */ +class RuleService { +private: + std::vector<Rule> m_rules; + +public: + /** + * Get the list of rules. + * + * \return the list of rules + */ + inline const std::vector<Rule> &list() const noexcept + { + return m_rules; + } + + /** + * Get the number of rules. + * + * \return the number of rules + */ + inline std::size_t length() const noexcept + { + return m_rules.size(); + } + + /** + * Append a rule. + * + * \param rule the rule to append + */ + IRCCD_EXPORT void add(Rule rule); + + /** + * Insert a new rule at the specified position. + * + * \param rule the rule + * \param position the position + */ + IRCCD_EXPORT void insert(Rule rule, unsigned position); + + /** + * Remove a new rule from the specified position. + * + * \pre position must be valid + * \param position the position + */ + IRCCD_EXPORT void remove(unsigned position); + + /** + * Resolve the action to execute with the specified list of rules. + * + * \param server the server name + * \param channel the channel name + * \param origin the origin + * \param plugin the plugin name + * \param event the event name (e.g onKick) + * \return true if the plugin must be called + */ + IRCCD_EXPORT bool solve(const std::string &server, + const std::string &channel, + const std::string &origin, + const std::string &plugin, + const std::string &event) noexcept; +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_RULE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-server.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,586 @@ +/* + * service-server.cpp -- manage IRC servers + * + * Copyright (c) 2013-2016 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 <algorithm> + +#include <json.hpp> +#include <format.h> + +#include "irccd.hpp" +#include "logger.hpp" +#include "plugin.hpp" +#include "server.hpp" +#include "service-plugin.hpp" +#include "service-rule.hpp" +#include "service-server.hpp" +#include "service-transport.hpp" +#include "util.hpp" + +using namespace fmt::literals; +using namespace nlohmann; + +namespace irccd { + +class EventHandler { +public: + std::string server; + std::string origin; + std::string target; + std::function<std::string (Plugin &)> functionName; + std::function<void (Plugin &)> functionExec; + + void operator()(Irccd &irccd) const + { + for (auto &plugin : irccd.plugins().list()) { + auto eventname = functionName(*plugin); + auto allowed = irccd.rules().solve(server, target, origin, plugin->name(), eventname); + + if (!allowed) { + log::debug() << "rule: event skipped on match" << std::endl; + continue; + } else + log::debug() << "rule: event allowed" << std::endl; + + // TODO: server-event must not know which type of plugin. + // TODO: get generic error. + // TODO: this is the responsability of service-plugin. + try { + functionExec(*plugin); + } catch (const std::exception &ex) { + log::warning() << "plugin " << plugin->name() << ": error: " << ex.what() << std::endl; + } + } + } +}; + +void ServerService::handleChannelMode(const ChannelModeEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onChannelMode:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " mode: " << ev.mode << "\n"; + log::debug() << " argument: " << ev.argument << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onChannelMode" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "mode", ev.mode }, + { "argument", ev.argument } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onChannelMode"; + }, + [=] (Plugin &plugin) { + plugin.onChannelMode(m_irccd, ev); + } + }); +} + +void ServerService::handleChannelNotice(const ChannelNoticeEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onChannelNotice:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " message: " << ev.message << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onChannelNotice" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "message", ev.message } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onChannelNotice"; + }, + [=] (Plugin &plugin) { + plugin.onChannelNotice(m_irccd, ev); + } + }); +} + +void ServerService::handleConnect(const ConnectEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onConnect" << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onConnect" }, + { "server", ev.server->name() } + })); + + m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "", + [=] (Plugin &) -> std::string { + return "onConnect"; + }, + [=] (Plugin &plugin) { + plugin.onConnect(m_irccd, ev); + } + }); +} + +void ServerService::handleInvite(const InviteEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onInvite:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " target: " << ev.nickname << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onInvite" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onInvite"; + }, + [=] (Plugin &plugin) { + plugin.onInvite(m_irccd, ev); + } + }); +} + +void ServerService::handleJoin(const JoinEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onJoin:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onJoin" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onJoin"; + }, + [=] (Plugin &plugin) { + plugin.onJoin(m_irccd, ev); + } + }); +} + +void ServerService::handleKick(const KickEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onKick:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " target: " << ev.target << "\n"; + log::debug() << " reason: " << ev.reason << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onKick" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "target", ev.target }, + { "reason", ev.reason } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onKick"; + }, + [=] (Plugin &plugin) { + plugin.onKick(m_irccd, ev); + } + }); +} + +void ServerService::handleMessage(const MessageEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onMessage:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " message: " << ev.message << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onMessage" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "message", ev.message } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &plugin) -> std::string { + return util::parseMessage(ev.message, ev.server->commandCharacter(), plugin.name()).second == util::MessageType::Command ? "onCommand" : "onMessage"; + }, + [=] (Plugin &plugin) mutable { + auto copy = ev; + auto pack = util::parseMessage(copy.message, copy.server->commandCharacter(), plugin.name()); + + copy.message = pack.first; + + if (pack.second == util::MessageType::Command) + plugin.onCommand(m_irccd, copy); + else + plugin.onMessage(m_irccd, copy); + } + }); +} + +void ServerService::handleMe(const MeEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onMe:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " target: " << ev.channel << "\n"; + log::debug() << " message: " << ev.message << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onMe" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "target", ev.channel }, + { "message", ev.message } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onMe"; + }, + [=] (Plugin &plugin) { + plugin.onMe(m_irccd, ev); + } + }); +} + +void ServerService::handleMode(const ModeEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onMode\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " mode: " << ev.mode << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onMode" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "mode", ev.mode } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "", + [=] (Plugin &) -> std::string { + return "onMode"; + }, + [=] (Plugin &plugin) { + plugin.onMode(m_irccd, ev); + } + }); +} + +void ServerService::handleNames(const NamesEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onNames:\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " names: " << util::join(ev.names.begin(), ev.names.end(), ", ") << std::endl; + + auto names = json::array(); + + for (const auto &v : ev.names) + names.push_back(v); + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onNames" }, + { "server", ev.server->name() }, + { "channel", ev.channel }, + { "names", std::move(names) } + })); + + m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", ev.channel, + [=] (Plugin &) -> std::string { + return "onNames"; + }, + [=] (Plugin &plugin) { + plugin.onNames(m_irccd, ev); + } + }); +} + +void ServerService::handleNick(const NickEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onNick:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " nickname: " << ev.nickname << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onNick" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "nickname", ev.nickname } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "", + [=] (Plugin &) -> std::string { + return "onNick"; + }, + [=] (Plugin &plugin) { + plugin.onNick(m_irccd, ev); + } + }); +} + +void ServerService::handleNotice(const NoticeEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onNotice:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " message: " << ev.message << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onNotice" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "message", ev.message } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "", + [=] (Plugin &) -> std::string { + return "onNotice"; + }, + [=] (Plugin &plugin) { + plugin.onNotice(m_irccd, ev); + } + }); +} + +void ServerService::handlePart(const PartEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onPart:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " reason: " << ev.reason << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onPart" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "reason", ev.reason } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onPart"; + }, + [=] (Plugin &plugin) { + plugin.onPart(m_irccd, ev); + } + }); +} + +void ServerService::handleQuery(const QueryEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onQuery:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " message: " << ev.message << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onQuery" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "message", ev.message } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "", + [=] (Plugin &plugin) -> std::string { + return util::parseMessage(ev.message, ev.server->commandCharacter(), plugin.name()).second == util::MessageType::Command ? "onQueryCommand" : "onQuery"; + }, + [=] (Plugin &plugin) mutable { + auto copy = ev; + auto pack = util::parseMessage(copy.message, copy.server->commandCharacter(), plugin.name()); + + copy.message = pack.first; + + if (pack.second == util::MessageType::Command) + plugin.onQueryCommand(m_irccd, copy); + else + plugin.onQuery(m_irccd, copy); + } + }); +} + +void ServerService::handleTopic(const TopicEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onTopic:\n"; + log::debug() << " origin: " << ev.origin << "\n"; + log::debug() << " channel: " << ev.channel << "\n"; + log::debug() << " topic: " << ev.topic << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onTopic" }, + { "server", ev.server->name() }, + { "origin", ev.origin }, + { "channel", ev.channel }, + { "topic", ev.topic } + })); + + m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel, + [=] (Plugin &) -> std::string { + return "onTopic"; + }, + [=] (Plugin &plugin) { + plugin.onTopic(m_irccd, ev); + } + }); +} + +void ServerService::handleWhois(const WhoisEvent &ev) +{ + log::debug() << "server " << ev.server->name() << ": event onWhois\n"; + log::debug() << " nickname: " << ev.whois.nick << "\n"; + log::debug() << " username: " << ev.whois.user << "\n"; + log::debug() << " host: " << ev.whois.host << "\n"; + log::debug() << " realname: " << ev.whois.realname << "\n"; + log::debug() << " channels: " << util::join(ev.whois.channels.begin(), ev.whois.channels.end()) << std::endl; + + m_irccd.transports().broadcast(nlohmann::json::object({ + { "event", "onWhois" }, + { "server", ev.server->name() }, + { "nickname", ev.whois.nick }, + { "username", ev.whois.user }, + { "host", ev.whois.host }, + { "realname", ev.whois.realname } + })); + + m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "", + [=] (Plugin &) -> std::string { + return "onWhois"; + }, + [=] (Plugin &plugin) { + plugin.onWhois(m_irccd, ev); + } + }); +} + +ServerService::ServerService(Irccd &irccd) + : m_irccd(irccd) +{ +} + +void ServerService::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + for (auto &server : m_servers) { + server->update(); + server->prepare(in, out, max); + } +} + +void ServerService::sync(fd_set &in, fd_set &out) +{ + for (auto &server : m_servers) + server->sync(in, out); +} + +bool ServerService::has(const std::string &name) const noexcept +{ + return std::count_if(m_servers.cbegin(), m_servers.end(), [&] (const auto &server) { + return server->name() == name; + }) > 0; +} + +void ServerService::add(std::shared_ptr<Server> server) +{ + assert(!has(server->name())); + + using namespace std::placeholders; + + std::weak_ptr<Server> ptr(server); + + server->onChannelMode.connect(std::bind(&ServerService::handleChannelMode, this, _1)); + server->onChannelNotice.connect(std::bind(&ServerService::handleChannelNotice, this, _1)); + server->onConnect.connect(std::bind(&ServerService::handleConnect, this, _1)); + server->onInvite.connect(std::bind(&ServerService::handleInvite, this, _1)); + server->onJoin.connect(std::bind(&ServerService::handleJoin, this, _1)); + server->onKick.connect(std::bind(&ServerService::handleKick, this, _1)); + server->onMessage.connect(std::bind(&ServerService::handleMessage, this, _1)); + server->onMe.connect(std::bind(&ServerService::handleMe, this, _1)); + server->onMode.connect(std::bind(&ServerService::handleMode, this, _1)); + server->onNames.connect(std::bind(&ServerService::handleNames, this, _1)); + server->onNick.connect(std::bind(&ServerService::handleNick, this, _1)); + server->onNotice.connect(std::bind(&ServerService::handleNotice, this, _1)); + server->onPart.connect(std::bind(&ServerService::handlePart, this, _1)); + server->onQuery.connect(std::bind(&ServerService::handleQuery, this, _1)); + server->onTopic.connect(std::bind(&ServerService::handleTopic, this, _1)); + server->onWhois.connect(std::bind(&ServerService::handleWhois, this, _1)); + server->onDie.connect([this, ptr] () { + m_irccd.post([=] (Irccd &) { + auto server = ptr.lock(); + + if (server) { + log::info("server {}: removed"_format(server->name())); + m_servers.erase(std::find(m_servers.begin(), m_servers.end(), server)); + } + }); + }); + + m_servers.push_back(std::move(server)); +} + +std::shared_ptr<Server> ServerService::get(const std::string &name) const noexcept +{ + auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) { + return server->name() == name; + }); + + if (it == m_servers.end()) + return nullptr; + + return *it; +} + +std::shared_ptr<Server> ServerService::require(const std::string &name) const +{ + auto server = get(name); + + if (!server) + throw std::invalid_argument("server {} not found"_format(name)); + + return server; +} + +void ServerService::remove(const std::string &name) +{ + auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) { + return server->name() == name; + }); + + if (it != m_servers.end()) { + (*it)->disconnect(); + m_servers.erase(it); + } +} + +void ServerService::clear() noexcept +{ + for (auto &server : m_servers) + server->disconnect(); + + m_servers.clear(); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-server.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,141 @@ +/* + * service-server.hpp -- manage IRC servers + * + * Copyright (c) 2013-2016 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_SERVICE_SERVER_HPP +#define IRCCD_SERVICE_SERVER_HPP + +/** + * \file service-server.hpp + * \brief Manage IRC servers. + */ + +#include <memory> +#include <set> +#include <string> + +#include "server.hpp" + +namespace irccd { + +class Irccd; + +/** + * \brief Manage IRC servers. + * \ingroup services + */ +class ServerService { +private: + Irccd &m_irccd; + std::vector<std::shared_ptr<Server>> m_servers; + + void handleChannelMode(const ChannelModeEvent &); + void handleChannelNotice(const ChannelNoticeEvent &); + void handleConnect(const ConnectEvent &); + void handleInvite(const InviteEvent &); + void handleJoin(const JoinEvent &); + void handleKick(const KickEvent &); + void handleMessage(const MessageEvent &); + void handleMe(const MeEvent &); + void handleMode(const ModeEvent &); + void handleNames(const NamesEvent &); + void handleNick(const NickEvent &); + void handleNotice(const NoticeEvent &); + void handlePart(const PartEvent &); + void handleQuery(const QueryEvent &); + void handleTopic(const TopicEvent &); + void handleWhois(const WhoisEvent &); + +public: + /** + * Create the server service. + */ + IRCCD_EXPORT ServerService(Irccd &instance); + + /** + * \copydoc Service::prepare + */ + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc Service::sync + */ + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); + + /** + * Get the list of servers + * + * \return the servers + */ + inline const std::vector<std::shared_ptr<Server>> &servers() const noexcept + { + return m_servers; + } + + /** + * Check if a server exists. + * + * \param name the name + * \return true if exists + */ + IRCCD_EXPORT bool has(const std::string &name) const noexcept; + + /** + * Add a new server to the application. + * + * \pre hasServer must return false + * \param sv the server + */ + IRCCD_EXPORT void add(std::shared_ptr<Server> sv); + + /** + * Get a server or empty one if not found + * + * \param name the server name + * \return the server or empty one if not found + */ + IRCCD_EXPORT std::shared_ptr<Server> get(const std::string &name) const noexcept; + + /** + * Find a server by name. + * + * \param name the server name + * \return the server + * \throw std::out_of_range if the server does not exist + */ + IRCCD_EXPORT std::shared_ptr<Server> require(const std::string &name) const; + + /** + * Remove a server from the irccd instance. + * + * The server if any, will be disconnected. + * + * \param name the server name + */ + IRCCD_EXPORT void remove(const std::string &name); + + /** + * Remove all servers. + * + * All servers will be disconnected. + */ + IRCCD_EXPORT void clear() noexcept; +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-transport.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,164 @@ +/* + * service-transport.cpp -- manage transport servers and clients + * + * Copyright (c) 2013-2016 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 "command.hpp" +#include "irccd.hpp" +#include "json.hpp" +#include "logger.hpp" +#include "service-command.hpp" +#include "service-transport.hpp" +#include "transport.hpp" + +namespace irccd { + +void TransportService::handleCommand(std::weak_ptr<TransportClient> ptr, const nlohmann::json &object) +{ + assert(object.is_object()); + + m_irccd.post([=] (Irccd &) { + // 0. Be sure the object still exists. + auto tc = ptr.lock(); + + if (!tc) + return; + + // 1. Check if the Json object is valid. + auto name = object.find("command"); + if (name == object.end() || !name->is_string()) { + // TODO: send error. + log::warning("invalid command object"); + return; + } + + // 2. Search for a command + auto cmd = m_irccd.commands().find(*name); + + if (!cmd) { + // TODO: send error again. + log::warning("command does not exists"); + return; + } + + // 3. Try to execute it. + auto response = nlohmann::json::object({}); + + try { + response = cmd->exec(m_irccd, object); + + // Adjust if command has returned something else. + if (!response.is_object()) + response = nlohmann::json::object({}); + + response.push_back({"status", true}); + } catch (const std::exception &ex) { + response.push_back({"status", false}); + response.push_back({"error", ex.what()}); + } + + // 4. Store the command name result. + response.push_back({"response", *name}); + + // 5. Send the result. + tc->send(response); + }); +} + +void TransportService::handleDie(std::weak_ptr<TransportClient> ptr) +{ + m_irccd.post([=] (Irccd &) { + log::info("transport: client disconnected"); + + auto tc = ptr.lock(); + + if (tc) + m_clients.erase(std::find(m_clients.begin(), m_clients.end(), tc)); + }); +} + +TransportService::TransportService(Irccd &irccd) noexcept + : m_irccd(irccd) +{ +} + +void TransportService::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + // Add transport servers. + for (const auto &transport : m_servers) { + FD_SET(transport->handle(), &in); + + if (transport->handle() > max) + max = transport->handle(); + } + + // Transport clients. + for (const auto &client : m_clients) + client->prepare(in, out, max); +} + +void TransportService::sync(fd_set &in, fd_set &out) +{ + using namespace std::placeholders; + + // Transport clients. + for (const auto &client : m_clients) { + try { + client->sync(in, out); + } catch (const std::exception &ex) { + log::info() << "transport: client disconnected: " << ex.what() << std::endl; + handleDie(client); + } + } + + // Transport servers. + for (const auto &transport : m_servers) { + if (!FD_ISSET(transport->handle(), &in)) + continue; + + log::debug("transport: new client connected"); + + std::shared_ptr<TransportClient> client = transport->accept(); + std::weak_ptr<TransportClient> ptr(client); + + try { + // Connect signals. + client->onCommand.connect(std::bind(&TransportService::handleCommand, this, ptr, _1)); + client->onDie.connect(std::bind(&TransportService::handleDie, this, ptr)); + + // Register it. + m_clients.push_back(std::move(client)); + } catch (const std::exception &ex) { + log::info() << "transport: client disconnected: " << ex.what() << std::endl; + } + } +} + +void TransportService::add(std::shared_ptr<TransportServer> ts) +{ + m_servers.push_back(std::move(ts)); +} + +void TransportService::broadcast(const nlohmann::json &json) +{ + assert(json.is_object()); + + for (const auto &client : m_clients) + if (client->state() == TransportClient::Ready) + client->send(json); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/service-transport.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,84 @@ +/* + * service-transport.hpp -- manage transport servers and clients + * + * Copyright (c) 2013-2016 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_SERVICE_TRANSPORT_HPP +#define IRCCD_SERVICE_TRANSPORT_HPP + +/** + * \file service-transport.hpp + * \brief manage transport servers and clients. + */ + +#include <json.hpp> + +namespace irccd { + +class TransportServer; +class TransportClient; + +/** + * \brief manage transport servers and clients. + * \ingroup services + */ +class TransportService { +private: + Irccd &m_irccd; + + std::vector<std::shared_ptr<TransportServer>> m_servers; + std::vector<std::shared_ptr<TransportClient>> m_clients; + + void handleCommand(std::weak_ptr<TransportClient>, const nlohmann::json &); + void handleDie(std::weak_ptr<TransportClient>); + +public: + /** + * Create the transport service. + * + * \param irccd the irccd instance + */ + IRCCD_EXPORT TransportService(Irccd &irccd) noexcept; + + /** + * \copydoc Service::prepare + */ + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc Service::sync + */ + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); + + /** + * Add a transport server. + * + * \param ts the transport server + */ + IRCCD_EXPORT void add(std::shared_ptr<TransportServer> ts); + + /** + * Send data to all clients. + * + * \pre object.is_object() + * \param object the json object + */ + IRCCD_EXPORT void broadcast(const nlohmann::json &object); +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_TRANSPORT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/transport.cpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,403 @@ +/* + * transport.cpp -- irccd transports + * + * Copyright (c) 2013-2016 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 <cassert> +#include <cstdio> + +#include "transport.hpp" + +namespace irccd { + +/* + * TransportClient + * ------------------------------------------------------------------ + */ + +void TransportClient::error(const std::string &msg) +{ + m_state = Closing; + + send({{ "error", msg }}); +} + +void TransportClient::flush() noexcept +{ + for (std::size_t pos; (pos = m_input.find("\r\n\r\n")) != std::string::npos; ) { + auto message = m_input.substr(0, pos); + + m_input.erase(m_input.begin(), m_input.begin() + pos + 4); + + try { + auto document = nlohmann::json::parse(message); + + if (!document.is_object()) + error("invalid argument"); + else + onCommand(document); + } catch (const std::exception &ex) { + error(ex.what()); + } + } +} + +void TransportClient::authenticate() noexcept +{ + auto pos = m_input.find("\r\n\r\n"); + + if (pos == std::string::npos) + return; + + auto msg = m_input.substr(0, pos); + + m_input.erase(m_input.begin(), m_input.begin() + pos + 4); + + try { + auto doc = nlohmann::json::parse(msg); + + if (!doc.is_object()) + error("invalid argument"); + + auto cmd = doc.find("command"); + + if (cmd == doc.end() || !cmd->is_string() || *cmd != "auth") + error("authentication required"); + + auto pw = doc.find("password"); + auto result = true; + + if (pw == doc.end() || !pw->is_string() || *pw != m_parent.password()) { + m_state = Closing; + result = false; + } else + m_state = Ready; + + send({ + { "response", "auth" }, + { "result", result } + }); + } catch (const std::exception &ex) { + error(ex.what()); + } +} + +void TransportClient::recv() noexcept +{ + try { + std::string buffer; + + buffer.resize(512); + buffer.resize(recv(&buffer[0], buffer.size())); + + if (buffer.empty()) + onDie(); + + m_input += std::move(buffer); + } catch (const std::exception &) { + onDie(); + } +} + +void TransportClient::send() noexcept +{ + try { + auto ns = send(&m_output[0], m_output.size()); + + if (ns == 0) + onDie(); + + m_output.erase(0, ns); + } catch (const std::exception &ex) { + onDie(); + } +} + +unsigned TransportClient::recv(void *buffer, unsigned length) +{ + return m_socket.recv(buffer, length); +} + +unsigned TransportClient::send(const void *buffer, unsigned length) +{ + return m_socket.send(buffer, length); +} + +TransportClient::TransportClient(TransportServer &parent, net::TcpSocket socket) + : m_parent(parent) + , m_socket(std::move(socket)) +{ + assert(m_socket.isOpen()); + + m_socket.set(net::option::SockBlockMode(false)); + + // Send some information. + auto object = nlohmann::json::object({ + { "program", "irccd" }, + { "major", IRCCD_VERSION_MAJOR }, + { "minor", IRCCD_VERSION_MINOR }, + { "patch", IRCCD_VERSION_PATCH } + }); + +#if defined(WITH_JS) + object.push_back({"javascript", true}); +#endif +#if defined(WITH_SSL) + object.push_back({"ssl", true}); +#endif + + send(object); +} + +void TransportClient::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + if (m_socket.handle() > max) + max = m_socket.handle(); + + switch (m_state) { + case Greeting: + FD_SET(m_socket.handle(), &out); + break; + case Authenticating: + FD_SET(m_socket.handle(), &in); + break; + case Ready: + FD_SET(m_socket.handle(), &in); + + if (!m_output.empty()) + FD_SET(m_socket.handle(), &out); + break; + case Closing: + if (!m_output.empty()) + FD_SET(m_socket.handle(), &out); + else + onDie(); + break; + default: + break; + } +} + +void TransportClient::sync(fd_set &in, fd_set &out) +{ + switch (m_state) { + case Greeting: + send(); + + if (m_output.empty()) + m_state = m_parent.password().empty() ? Ready : Authenticating; + + break; + case Authenticating: + if (FD_ISSET(m_socket.handle(), &in)) + recv(); + + authenticate(); + break; + case Ready: + if (FD_ISSET(m_socket.handle(), &in)) + recv(); + if (FD_ISSET(m_socket.handle(), &out)) + send(); + + flush(); + break; + case Closing: + if (FD_ISSET(m_socket.handle(), &out)) + send(); + break; + default: + break; + } +} + +void TransportClient::send(const nlohmann::json &json) +{ + assert(json.is_object()); + + m_output += json.dump(); + m_output += "\r\n\r\n"; +} + +/* + * TransportClientTls + * ------------------------------------------------------------------ + */ + +void TransportClientTls::handshake() +{ + try { + m_ssl.handshake(); + m_handshake = HandshakeReady; + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } catch (const std::exception &) { + onDie(); + } +} + +TransportClientTls::TransportClientTls(const std::string &pkey, + const std::string &cert, + TransportServer &server, + net::TcpSocket socket) + : TransportClient(server, std::move(socket)) + , m_ssl(m_socket) +{ + m_ssl.setPrivateKey(pkey); + m_ssl.setCertificate(cert); + + handshake(); +} + +unsigned TransportClientTls::recv(void *buffer, unsigned length) +{ + unsigned nread = 0; + + try { + nread = m_ssl.recv(buffer, length); + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } + + return nread; +} + +unsigned TransportClientTls::send(const void *buffer, unsigned length) +{ + unsigned nsent = 0; + + try { + nsent = m_ssl.send(buffer, length); + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } + + return nsent; +} + +void TransportClientTls::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + if (m_socket.handle() > max) + max = m_socket.handle(); + + switch (m_handshake) { + case HandshakeRead: + FD_SET(m_socket.handle(), &in); + break; + case HandshakeWrite: + FD_SET(m_socket.handle(), &out); + break; + default: + TransportClient::prepare(in, out, max); + break; + } +} + +void TransportClientTls::sync(fd_set &in, fd_set &out) +{ + switch (m_handshake) { + case HandshakeRead: + case HandshakeWrite: + handshake(); + break; + default: + TransportClient::sync(in, out); + } +} + +/* + * TransportServerIp + * ------------------------------------------------------------------ + */ + +TransportServerIp::TransportServerIp(const std::string &address, + std::uint16_t port, + std::uint8_t mode) + : TransportServer(net::TcpSocket((mode & v6) ? AF_INET6 : AF_INET, 0)) +{ + assert((mode & v6) || (mode & v4)); + + m_socket.set(net::option::SockReuseAddress(true)); + + if (mode & v6) { + if (address == "*") + m_socket.bind(net::ipv6::any(port)); + else + m_socket.bind(net::ipv6::pton(address, port)); + + // Disable or enable IPv4 when using IPv6. + if (!(mode & v4)) + m_socket.set(net::option::Ipv6Only(true)); + } else { + if (address == "*") + m_socket.bind(net::ipv4::any(port)); + else + m_socket.bind(net::ipv4::pton(address, port)); + } + + m_socket.listen(); +} + +/* + * TransportServerTls + * ------------------------------------------------------------------ + */ + +TransportServerTls::TransportServerTls(const std::string &pkey, + const std::string &cert, + const std::string &address, + std::uint16_t port, + std::uint8_t mode) + : TransportServerIp(address, port, mode) + , m_privatekey(pkey) + , m_cert(cert) +{ +} + +std::unique_ptr<TransportClient> TransportServerTls::accept() +{ + return std::make_unique<TransportClientTls>(m_privatekey, m_cert, *this, m_socket.accept()); +} + +/* + * TransportServerLocal + * ------------------------------------------------------------------ + */ + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +TransportServerLocal::TransportServerLocal(std::string path) + : TransportServer(net::TcpSocket(AF_LOCAL, 0)) + , m_path(std::move(path)) +{ + m_socket.bind(net::local::create(m_path, true)); + m_socket.listen(); +} + +TransportServerLocal::~TransportServerLocal() +{ + ::remove(m_path.c_str()); +} + +#endif + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/transport.hpp Wed Oct 05 13:27:15 2016 +0200 @@ -0,0 +1,388 @@ +/* + * transport.hpp -- irccd transports + * + * Copyright (c) 2013-2016 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_TRANSPORT_HPP +#define IRCCD_TRANSPORT_HPP + +/** + * \file transport.hpp + * \brief Irccd transports. + */ + +#include <cstdint> +#include <memory> +#include <string> + +#include <json.hpp> + +#include "net.hpp" +#include "signals.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +class TransportServer; + +/** + * \class TransportClient + * \brief Client connected to irccd. + * + * This class emits a warning upon clients request through onCommand signal. + */ +class TransportClient { +public: + /** + * \brief Client state + */ + enum State { + Greeting, //!< client is getting irccd info + Authenticating, //!< client requires authentication + Ready, //!< client is ready to use + Closing //!< client must disconnect + }; + + /** + * Signal: onCommand + * ---------------------------------------------------------- + * + * Arguments: + * - the command + */ + Signal<const nlohmann::json &> onCommand; + + /** + * Signal: onDie + * ---------------------------------------------------------- + * + * The client has disconnected. + */ + Signal<> onDie; + +private: + void error(const std::string &msg); + void flush() noexcept; + void authenticate() noexcept; + +protected: + State m_state{Greeting}; //!< current client state + TransportServer &m_parent; //!< parent transport server + net::TcpSocket m_socket; //!< socket + std::string m_input; //!< input buffer + std::string m_output; //!< output buffer + + /** + * Fill the input buffer with available data. + */ + void recv() noexcept; + + /** + * Flush the output buffer from available pending data. + */ + void send() noexcept; + + /** + * Try to receive some data into the given buffer. + * + * \param buffer the destination buffer + * \param length the buffer length + * \return the number of bytes received + */ + IRCCD_EXPORT virtual unsigned recv(void *buffer, unsigned length); + + /** + * Try to send some data into the given buffer. + * + * \param buffer the source buffer + * \param length the buffer length + * \return the number of bytes sent + */ + IRCCD_EXPORT virtual unsigned send(const void *buffer, unsigned length); + +public: + /** + * Create a transport client from the socket. + * + * \pre socket must be valid + * \param parent the parent server + * \param socket the new socket + */ + IRCCD_EXPORT TransportClient(TransportServer &parent, net::TcpSocket socket); + + /** + * Virtual destructor defaulted. + */ + virtual ~TransportClient() = default; + + /** + * Get the client state. + * + * \return the client state + */ + inline State state() const noexcept + { + return m_state; + } + + /** + * Append some data to the output queue. + * + * \pre json.is_object() + * \param json the json object + */ + IRCCD_EXPORT void send(const nlohmann::json &json); + + /** + * \copydoc Service::prepare + */ + IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc Service::sync + */ + IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); +}; + +/* + * TransportClientTls + * ------------------------------------------------------------------ + */ + +/** + * \brief TLS version of transport client. + */ +class TransportClientTls : public TransportClient { +private: + enum { + HandshakeWrite, + HandshakeRead, + HandshakeReady + } m_handshake{HandshakeReady}; + + net::TlsSocket m_ssl; + + void handshake(); + +protected: + /** + * \copydoc TransportClient::recv + */ + unsigned recv(void *buffer, unsigned length) override; + + /** + * \copydoc TransportClient::send + */ + unsigned send(const void *buffer, unsigned length) override; + +public: + /** + * Create the transport client. + * + * \pre socket.isOpen() + * \param pkey the private key + * \param cert the certificate file + * \param socket the accepted socket + * \param parent the parent server + * \param socket the new socket + */ + IRCCD_EXPORT TransportClientTls(const std::string &pkey, + const std::string &cert, + TransportServer &server, + net::TcpSocket socket); + + /** + * \copydoc TransportClient::prepare + */ + IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc TransportClient::sync + */ + IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); +}; + +/* + * TransportServer + * ------------------------------------------------------------------ + */ + +/** + * \brief Bring networking between irccd and irccdctl. + * + * This class contains a master sockets for listening to TCP connections, it is + * then processed by irccd. + * + * The transport class supports the following domains: + * + * | Domain | Class | + * |-----------------------|-----------------------| + * | IPv4, IPv6 | TransportServerIp | + * | Unix (not on Windows) | TransportServerUnix | + * + * Note: IPv4 and IPv6 can be combined, using TransportServer::IPv6 and its + * option. + */ +class TransportServer { +private: + TransportServer(const TransportServer &) = delete; + TransportServer(TransportServer &&) = delete; + + TransportServer &operator=(const TransportServer &) = delete; + TransportServer &operator=(TransportServer &&) = delete; + +protected: + net::TcpSocket m_socket; + std::string m_password; + +public: + /** + * Default constructor. + */ + inline TransportServer(net::TcpSocket socket) + : m_socket(std::move(socket)) + { + } + + /** + * Get the socket handle for this transport. + * + * \return the handle + */ + inline net::Handle handle() const noexcept + { + return m_socket.handle(); + } + + /** + * Get the password. + * + * \return the password + */ + inline const std::string &password() const noexcept + { + return m_password; + } + + /** + * Set an optional password. + * + * \return the password + */ + inline void setPassword(std::string password) noexcept + { + m_password = std::move(password); + } + + /** + * Destructor defaulted. + */ + virtual ~TransportServer() = default; + + /** + * Accept a new client depending on the domain. + * + * \return the new client + */ + virtual std::unique_ptr<TransportClient> accept() + { + return std::make_unique<TransportClient>(*this, m_socket.accept()); + } +}; + +/** + * \brief Create IP transport. + */ +class TransportServerIp : public TransportServer { +public: + /** + * \brief Domain to use. + */ + enum Mode { + v4 = (1 << 0), //!< IPv6 + v6 = (1 << 1) //!< IPv4 + }; + + /** + * Constructor. + * \pre mode > 0 + * \param address the address (* for any) + * \param port the port number + * \param mode the domains to use (can be OR'ed) + */ + IRCCD_EXPORT TransportServerIp(const std::string &address, + std::uint16_t port, + std::uint8_t mode = v4); +}; + +/** + * \brief TLS over IP transport. + */ +class TransportServerTls : public TransportServerIp { +private: + std::string m_privatekey; + std::string m_cert; + +public: + /** + * Constructor. + * \pre mode > 0 + * \param pkey the private key file + * \param cert the certificate file + * \param address the address (* for any) + * \param port the port number + * \param mode the domains to use (can be OR'ed) + */ + IRCCD_EXPORT TransportServerTls(const std::string &pkey, + const std::string &cert, + const std::string &address, + std::uint16_t port, + std::uint8_t mode = v4); + + /** + * \copydoc TransportServer::accept + */ + IRCCD_EXPORT std::unique_ptr<TransportClient> accept() override; +}; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +/** + * \brief Implementation of transports for Unix sockets. + */ +class TransportServerLocal : public TransportServer { +private: + std::string m_path; + +public: + /** + * Create a Unix transport. + * + * \param path the path + */ + IRCCD_EXPORT TransportServerLocal(std::string path); + + /** + * Destroy the transport and remove the file. + */ + IRCCD_EXPORT ~TransportServerLocal(); +}; + +#endif // !IRCCD_SYSTEM_WINDOWS + +} // !irccd + +#endif // !IRCCD_TRANSPORT_HPP