Mercurial > irccd
view libirccd/irccd/daemon/service/server_service.cpp @ 648:8a9cac75608f
Irccd: move parse_message from string_util to server_util, closes #783 @5m
While here, fix some style issue.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 26 Mar 2018 19:22:01 +0200 |
parents | 23fd8bad4006 |
children | e37ac2269ba6 |
line wrap: on
line source
/* * server_service.cpp -- server service * * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <irccd/json_util.hpp> #include <irccd/string_util.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/logger.hpp> #include <irccd/daemon/server_util.hpp> #include <irccd/daemon/service/plugin_service.hpp> #include <irccd/daemon/service/rule_service.hpp> #include <irccd/daemon/service/server_service.hpp> #include <irccd/daemon/service/transport_service.hpp> namespace irccd { namespace { template <typename EventNameFunc, typename ExecFunc> void dispatch(irccd& daemon, const std::string& server, const std::string& origin, const std::string& target, EventNameFunc&& name_func, ExecFunc exec_func) { for (auto& plugin : daemon.plugins().list()) { auto eventname = name_func(*plugin); auto allowed = daemon.rules().solve(server, target, origin, plugin->get_name(), eventname); if (!allowed) { daemon.log().debug("rule: event skipped on match"); continue; } daemon.log().debug("rule: event allowed"); try { exec_func(*plugin); } catch (const std::exception& ex) { daemon.log().warning() << "plugin " << plugin->get_name() << ": error: " << ex.what() << std::endl; } } } } // !namespace void server_service::handle_connect(const connect_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onConnect" << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onConnect" }, { "server", ev.server->get_name() } })); dispatch(irccd_, ev.server->get_name(), /* origin */ "", /* channel */ "", [=] (plugin&) -> std::string { return "onConnect"; }, [=] (plugin& plugin) { plugin.handle_connect(irccd_, ev); } ); } void server_service::handle_die(const disconnect_event& ev) { // First, remove the server in case of exceptions. servers_.erase(std::find(servers_.begin(), servers_.end(), ev.server)); irccd_.log().debug() << "server " << ev.server->get_name() << ": event onDisconnect" << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onDisconnect" }, { "server", ev.server->get_name() } })); dispatch(irccd_, ev.server->get_name(), /* origin */ "", /* channel */ "", [=] (plugin&) -> std::string { return "onDisconnect"; }, [=] (plugin& plugin) { plugin.handle_disconnect(irccd_, ev); } ); } void server_service::handle_invite(const invite_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onInvite:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " target: " << ev.nickname << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onInvite" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onInvite"; }, [=] (plugin& plugin) { plugin.handle_invite(irccd_, ev); } ); } void server_service::handle_join(const join_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onJoin:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onJoin" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onJoin"; }, [=] (plugin& plugin) { plugin.handle_join(irccd_, ev); } ); } void server_service::handle_kick(const kick_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onKick:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " target: " << ev.target << "\n"; irccd_.log().debug() << " reason: " << ev.reason << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onKick" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "target", ev.target }, { "reason", ev.reason } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onKick"; }, [=] (plugin& plugin) { plugin.handle_kick(irccd_, ev); } ); } void server_service::handle_message(const message_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onMessage:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " message: " << ev.message << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onMessage" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "message", ev.message } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin& plugin) -> std::string { return server_util::parse_message( ev.message, ev.server->get_command_char(), plugin.get_name() ).type == server_util::message_pack::type::command ? "onCommand" : "onMessage"; }, [=] (plugin& plugin) mutable { auto copy = ev; auto pack = server_util::parse_message( copy.message, copy.server->get_command_char(), plugin.get_name() ); copy.message = pack.message; if (pack.type == server_util::message_pack::type::command) plugin.handle_command(irccd_, copy); else plugin.handle_message(irccd_, copy); } ); } void server_service::handle_me(const me_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onMe:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " target: " << ev.channel << "\n"; irccd_.log().debug() << " message: " << ev.message << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onMe" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "target", ev.channel }, { "message", ev.message } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onMe"; }, [=] (plugin& plugin) { plugin.handle_me(irccd_, ev); } ); } void server_service::handle_mode(const mode_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onMode\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " mode: " << ev.mode << "\n"; irccd_.log().debug() << " limit: " << ev.limit << "\n"; irccd_.log().debug() << " user: " << ev.user << "\n"; irccd_.log().debug() << " mask: " << ev.mask << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onMode" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "mode", ev.mode }, { "limit", ev.limit }, { "user", ev.user }, { "mask", ev.mask } })); dispatch(irccd_, ev.server->get_name(), ev.origin, /* channel */ "", [=] (plugin &) -> std::string { return "onMode"; }, [=] (plugin &plugin) { plugin.handle_mode(irccd_, ev); } ); } void server_service::handle_names(const names_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onNames:\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " names: " << string_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); irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onNames" }, { "server", ev.server->get_name() }, { "channel", ev.channel }, { "names", std::move(names) } })); dispatch(irccd_, ev.server->get_name(), /* origin */ "", ev.channel, [=] (plugin&) -> std::string { return "onNames"; }, [=] (plugin& plugin) { plugin.handle_names(irccd_, ev); } ); } void server_service::handle_nick(const nick_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onNick:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " nickname: " << ev.nickname << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onNick" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "nickname", ev.nickname } })); dispatch(irccd_, ev.server->get_name(), ev.origin, /* channel */ "", [=] (plugin&) -> std::string { return "onNick"; }, [=] (plugin& plugin) { plugin.handle_nick(irccd_, ev); } ); } void server_service::handle_notice(const notice_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onNotice:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " message: " << ev.message << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onNotice" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "message", ev.message } })); dispatch(irccd_, ev.server->get_name(), ev.origin, /* channel */ "", [=] (plugin&) -> std::string { return "onNotice"; }, [=] (plugin& plugin) { plugin.handle_notice(irccd_, ev); } ); } void server_service::handle_part(const part_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onPart:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " reason: " << ev.reason << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onPart" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "reason", ev.reason } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onPart"; }, [=] (plugin& plugin) { plugin.handle_part(irccd_, ev); } ); } void server_service::handle_topic(const topic_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onTopic:\n"; irccd_.log().debug() << " origin: " << ev.origin << "\n"; irccd_.log().debug() << " channel: " << ev.channel << "\n"; irccd_.log().debug() << " topic: " << ev.topic << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onTopic" }, { "server", ev.server->get_name() }, { "origin", ev.origin }, { "channel", ev.channel }, { "topic", ev.topic } })); dispatch(irccd_, ev.server->get_name(), ev.origin, ev.channel, [=] (plugin&) -> std::string { return "onTopic"; }, [=] (plugin& plugin) { plugin.handle_topic(irccd_, ev); } ); } void server_service::handle_whois(const whois_event& ev) { irccd_.log().debug() << "server " << ev.server->get_name() << ": event onWhois\n"; irccd_.log().debug() << " nickname: " << ev.whois.nick << "\n"; irccd_.log().debug() << " username: " << ev.whois.user << "\n"; irccd_.log().debug() << " host: " << ev.whois.host << "\n"; irccd_.log().debug() << " realname: " << ev.whois.realname << "\n"; irccd_.log().debug() << " channels: " << string_util::join(ev.whois.channels, ", ") << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onWhois" }, { "server", ev.server->get_name() }, { "nickname", ev.whois.nick }, { "username", ev.whois.user }, { "host", ev.whois.host }, { "realname", ev.whois.realname } })); dispatch(irccd_, ev.server->get_name(), /* origin */ "", /* channel */ "", [=] (plugin&) -> std::string { return "onWhois"; }, [=] (plugin& plugin) { plugin.handle_whois(irccd_, ev); } ); } server_service::server_service(irccd &irccd) : irccd_(irccd) { } bool server_service::has(const std::string& name) const noexcept { return std::count_if(servers_.begin(), servers_.end(), [&] (const auto& server) { return server->get_name() == name; }) > 0; } void server_service::add(std::shared_ptr<server> server) { assert(!has(server->get_name())); server->on_connect.connect(boost::bind(&server_service::handle_connect, this, _1)); server->on_die.connect(boost::bind(&server_service::handle_die, this, _1)); server->on_invite.connect(boost::bind(&server_service::handle_invite, this, _1)); server->on_join.connect(boost::bind(&server_service::handle_join, this, _1)); server->on_kick.connect(boost::bind(&server_service::handle_kick, this, _1)); server->on_message.connect(boost::bind(&server_service::handle_message, this, _1)); server->on_me.connect(boost::bind(&server_service::handle_me, this, _1)); server->on_mode.connect(boost::bind(&server_service::handle_mode, this, _1)); server->on_names.connect(boost::bind(&server_service::handle_names, this, _1)); server->on_nick.connect(boost::bind(&server_service::handle_nick, this, _1)); server->on_notice.connect(boost::bind(&server_service::handle_notice, this, _1)); server->on_part.connect(boost::bind(&server_service::handle_part, this, _1)); server->on_topic.connect(boost::bind(&server_service::handle_topic, this, _1)); server->on_whois.connect(boost::bind(&server_service::handle_whois, this, _1)); server->connect(); servers_.push_back(std::move(server)); } std::shared_ptr<server> server_service::get(const std::string& name) const noexcept { auto it = std::find_if(servers_.begin(), servers_.end(), [&] (const auto& server) { return server->get_name() == name; }); if (it == servers_.end()) return nullptr; return *it; } std::shared_ptr<server> server_service::require(const std::string& name) const { if (!string_util::is_identifier(name)) throw server_error(server_error::invalid_identifier); const auto s = get(name); if (!s) throw server_error(server_error::not_found); return s; } void server_service::remove(const std::string& name) { auto it = std::find_if(servers_.begin(), servers_.end(), [&] (const auto& server) { return server->get_name() == name; }); if (it != servers_.end()) { (*it)->disconnect(); servers_.erase(it); } } void server_service::clear() noexcept { /* * Copy the array, because disconnect() may trigger on_die signal which * erase the server from itself. */ const auto save = servers_; for (const auto& server : save) server->disconnect(); servers_.clear(); } void server_service::load(const config& cfg) noexcept { for (const auto& section : cfg.doc()) { if (section.key() != "server") continue; try { add(server_util::from_config(irccd_.service(), cfg, section)); } catch (const std::exception& ex) { irccd_.log().warning() << "server " << section.get("name").value() << ": " << ex.what() << std::endl; } } } } // !irccd