Mercurial > irccd
changeset 347:ec43b9ac3df7
Irccd: unify services
line wrap: on
line diff
--- a/irccd/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/irccd/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -39,16 +39,11 @@ #include "logger.hpp" #include "options.hpp" #include "path.hpp" +#include "service.hpp" #include "system.hpp" #include "config.hpp" #include "irccd.hpp" -#include "service-command.hpp" -#include "service-plugin.hpp" -#include "service-rule.hpp" -#include "service-server.hpp" -#include "service-transport.hpp" - #if defined(WITH_JS) # include "mod-directory.hpp" # include "mod-elapsed-timer.hpp"
--- a/libirccd-js/irccd/mod-plugin.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd-js/irccd/mod-plugin.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -18,7 +18,7 @@ #include "irccd.hpp" #include "plugin-js.hpp" -#include "service-plugin.hpp" +#include "service.hpp" #include "mod-irccd.hpp" #include "mod-plugin.hpp"
--- a/libirccd-js/irccd/mod-server.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd-js/irccd/mod-server.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -25,7 +25,7 @@ #include "mod-server.hpp" #include "plugin-js.hpp" #include "server.hpp" -#include "service-server.hpp" +#include "service.hpp" namespace irccd {
--- a/libirccd-js/irccd/plugin-js.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd-js/irccd/plugin-js.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -30,7 +30,7 @@ #include "mod-plugin.hpp" #include "mod-server.hpp" #include "plugin-js.hpp" -#include "service-plugin.hpp" +#include "service.hpp" #include "timer.hpp" namespace irccd {
--- a/libirccd-test/irccd/command-tester.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd-test/irccd/command-tester.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,9 +19,7 @@ #include "command-tester.hpp" #include "client.hpp" #include "logger.hpp" -#include "service-command.hpp" -#include "service-server.hpp" -#include "service-transport.hpp" +#include "service.hpp" #include "transport.hpp" namespace irccd {
--- a/libirccd/CMakeLists.txt Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd/CMakeLists.txt Sun Nov 13 10:41:28 2016 +0100 @@ -10,12 +10,7 @@ ${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/service.hpp ${libirccd_SOURCE_DIR}/irccd/transport.hpp ) @@ -27,12 +22,7 @@ ${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/service.cpp ${libirccd_SOURCE_DIR}/irccd/transport.cpp )
--- a/libirccd/irccd/command.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd/irccd/command.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -18,8 +18,7 @@ #include "command.hpp" #include "irccd.hpp" -#include "service-plugin.hpp" -#include "service-server.hpp" +#include "service.hpp" #include "transport.hpp" #include "util.hpp"
--- a/libirccd/irccd/config.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd/irccd/config.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -28,7 +28,7 @@ #include "plugin.hpp" #include "rule.hpp" #include "server.hpp" -#include "service-plugin.hpp" +#include "service.hpp" #include "sysconfig.hpp" #include "transport.hpp" #include "util.hpp"
--- a/libirccd/irccd/irccd.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/libirccd/irccd/irccd.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,12 +19,7 @@ #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 "service.hpp" #include "util.hpp" using namespace std;
--- a/libirccd/irccd/service-command.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * 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; -} - -void CommandService::add(std::shared_ptr<Command> command) -{ - auto it = std::find_if(m_commands.begin(), m_commands.end(), [&] (const auto &cmd) { - return cmd->name() == command->name(); - }); - - if (it != m_commands.end()) - *it = std::move(command); - else - m_commands.push_back(std::move(command)); -} - -} // !irccd
--- a/libirccd/irccd/service-command.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * 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; - - /** - * Add a command or replace existing one. - * - * \pre command != nullptr - * \param command the command name - */ - IRCCD_EXPORT void add(std::shared_ptr<Command> command); -}; - -} // !irccd - -#endif // !IRCCD_SERVICE_COMMAND_HPP
--- a/libirccd/irccd/service-interrupt.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-interrupt.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-plugin.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,183 +0,0 @@ -/* - * 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::addLoader(std::unique_ptr<PluginLoader> loader) -{ - m_loaders.push_back(std::move(loader)); -} - -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
--- a/libirccd/irccd/service-plugin.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/* - * 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); - - /** - * Add a loader. - * - * \param loader the loader - */ - IRCCD_EXPORT void addLoader(std::unique_ptr<PluginLoader> loader); - - /** - * 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
--- a/libirccd/irccd/service-rule.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-rule.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-server.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,586 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-server.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/* - * 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
--- a/libirccd/irccd/service-transport.cpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * 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; - - auto name = object.find("command"); - if (name == object.end() || !name->is_string()) { - // TODO: send error. - log::warning("invalid command object"); - return; - } - - auto cmd = m_irccd.commands().find(*name); - - if (!cmd) - tc->error(*name, "command does not exist"); - else { - try { - cmd->exec(m_irccd, *tc, object); - } catch (const std::exception &ex) { - tc->error(cmd->name(), ex.what()); - } - } - }); -} - -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
--- a/libirccd/irccd/service-transport.hpp Sun Nov 13 10:00:20 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * 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/service.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -0,0 +1,999 @@ +/* + * service.cpp -- irccd services + * + * 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 <array> +#include <functional> +#include <stdexcept> + +#include <format.h> + +#include "irccd.hpp" +#include "logger.hpp" +#include "service.hpp" +#include "transport.hpp" + +using namespace fmt::literals; + +namespace irccd { + +/* + * CommandService. + * ------------------------------------------------------------------ + */ + +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; +} + +void CommandService::add(std::shared_ptr<Command> command) +{ + auto it = std::find_if(m_commands.begin(), m_commands.end(), [&] (const auto &cmd) { + return cmd->name() == command->name(); + }); + + if (it != m_commands.end()) + *it = std::move(command); + else + m_commands.push_back(std::move(command)); +} + +/* + * InterruptService. + * ------------------------------------------------------------------ + */ + +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; + } +} + +/* + * PluginService. + * ------------------------------------------------------------------ + */ + +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::addLoader(std::unique_ptr<PluginLoader> loader) +{ + m_loaders.push_back(std::move(loader)); +} + +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); + } +} + +/* + * RuleService. + * ------------------------------------------------------------------ + */ + +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; +} + +/* + * ServerService. + * ------------------------------------------------------------------ + */ + +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 = nlohmann::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(); +} + +/* + * TransportService. + * ------------------------------------------------------------------ + */ + +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; + + auto name = object.find("command"); + if (name == object.end() || !name->is_string()) { + // TODO: send error. + log::warning("invalid command object"); + return; + } + + auto cmd = m_irccd.commands().find(*name); + + if (!cmd) + tc->error(*name, "command does not exist"); + else { + try { + cmd->exec(m_irccd, *tc, object); + } catch (const std::exception &ex) { + tc->error(cmd->name(), ex.what()); + } + } + }); +} + +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.hpp Sun Nov 13 10:41:28 2016 +0100 @@ -0,0 +1,531 @@ +/* + * service.hpp -- irccd services + * + * 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_HPP +#define IRCCD_SERVICE_HPP + +/** + * \file service.hpp + * \brief Irccd services. + */ + +#include <memory> +#include <unordered_map> +#include <vector> + +#include <json.hpp> + +#include "command.hpp" +#include "net.hpp" +#include "plugin.hpp" +#include "rule.hpp" +#include "server.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +/* + * CommandService. + * ------------------------------------------------------------------ + */ + +/** + * \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; + + /** + * Add a command or replace existing one. + * + * \pre command != nullptr + * \param command the command name + */ + IRCCD_EXPORT void add(std::shared_ptr<Command> command); +}; + +/* + * InterruptService. + * ------------------------------------------------------------------ + */ + +/** + * \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; +}; + +/* + * PluginService. + * ------------------------------------------------------------------ + */ + +/** + * \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); + + /** + * Add a loader. + * + * \param loader the loader + */ + IRCCD_EXPORT void addLoader(std::unique_ptr<PluginLoader> loader); + + /** + * 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); +}; + +/* + * RuleService. + * ------------------------------------------------------------------ + */ + +/** + * \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; +}; + +/* + * ServerService. + * ------------------------------------------------------------------ + */ + +/** + * \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; +}; + +/* + * TransportService. + * ------------------------------------------------------------------ + */ + +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_HPP
--- a/tests/cmd-plugin-config/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-config/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> using namespace irccd; using namespace irccd::command;
--- a/tests/cmd-plugin-info/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-info/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> #include <plugin.hpp> using namespace irccd;
--- a/tests/cmd-plugin-list/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-list/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> #include <plugin.hpp> using namespace irccd;
--- a/tests/cmd-plugin-load/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-load/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> #include <plugin.hpp> using namespace irccd;
--- a/tests/cmd-plugin-reload/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-reload/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> #include <plugin.hpp> using namespace irccd;
--- a/tests/cmd-plugin-unload/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-plugin-unload/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-plugin.hpp> +#include <service.hpp> #include <plugin.hpp> using namespace irccd;
--- a/tests/cmd-server-connect/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-server-connect/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-server.hpp> +#include <service.hpp> using namespace irccd; using namespace irccd::command;
--- a/tests/cmd-server-disconnect/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-server-disconnect/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-server.hpp> +#include <service.hpp> #include <server.hpp> using namespace irccd;
--- a/tests/cmd-server-info/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-server-info/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-server.hpp> +#include <service.hpp> using namespace irccd; using namespace irccd::command;
--- a/tests/cmd-server-list/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-server-list/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-server.hpp> +#include <service.hpp> using namespace irccd; using namespace irccd::command;
--- a/tests/cmd-server-reconnect/main.cpp Sun Nov 13 10:00:20 2016 +0100 +++ b/tests/cmd-server-reconnect/main.cpp Sun Nov 13 10:41:28 2016 +0100 @@ -19,7 +19,7 @@ #include <command.hpp> #include <command-tester.hpp> #include <server-tester.hpp> -#include <service-server.hpp> +#include <service.hpp> using namespace irccd; using namespace irccd::command;