Mercurial > irccd
view libirccd/irccd/service-server.cpp @ 291:b490853404d9
Irccd: split lib into libirccd, #564
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 05 Oct 2016 13:27:15 +0200 |
parents | |
children |
line wrap: on
line source
/* * 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