changeset 125:ef4bef0bf0f8

Irccd: implement ServerService, #497
author David Demelier <markand@malikania.fr>
date Tue, 10 May 2016 22:51:10 +0200
parents 0895acad4072
children 49572a69c41d
files irccd/main.cpp lib/irccd/CMakeSources.cmake lib/irccd/cmd-server-cmode.cpp lib/irccd/cmd-server-cnotice.cpp lib/irccd/cmd-server-connect.cpp lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-invite.cpp lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-me.cpp lib/irccd/cmd-server-message.cpp lib/irccd/cmd-server-mode.cpp lib/irccd/cmd-server-nick.cpp lib/irccd/cmd-server-notice.cpp lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-topic.cpp lib/irccd/irccd.cpp lib/irccd/irccd.hpp lib/irccd/js-server.cpp lib/irccd/service-server.cpp lib/irccd/service-server.hpp
diffstat 24 files changed, 882 insertions(+), 753 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/main.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/irccd/main.cpp	Tue May 10 22:51:10 2016 +0200
@@ -37,6 +37,7 @@
 #include <irccd/logger.hpp>
 #include <irccd/options.hpp>
 #include <irccd/path.hpp>
+#include <irccd/service-server.hpp>
 #include <irccd/system.hpp>
 #include <irccd/config.hpp>
 #include <irccd/irccd.hpp>
@@ -232,7 +233,7 @@
 
 	// [server] section.
 	for (const auto &server : config.loadServers()) {
-		instance->addServer(server);
+		instance->serverService().addServer(server);
 	}
 
 	// [rule] section.
--- a/lib/irccd/CMakeSources.cmake	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/CMakeSources.cmake	Tue May 10 22:51:10 2016 +0200
@@ -64,6 +64,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.hpp
 	${CMAKE_CURRENT_LIST_DIR}/service.hpp
 	${CMAKE_CURRENT_LIST_DIR}/service-interrupt.hpp
+	${CMAKE_CURRENT_LIST_DIR}/service-server.hpp
 	${CMAKE_CURRENT_LIST_DIR}/sockets.hpp
 	${CMAKE_CURRENT_LIST_DIR}/system.hpp
 	${CMAKE_CURRENT_LIST_DIR}/timer.hpp
@@ -132,6 +133,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/server-state-connecting.cpp
 	${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.cpp
 	${CMAKE_CURRENT_LIST_DIR}/service-interrupt.cpp
+	${CMAKE_CURRENT_LIST_DIR}/service-server.cpp
 	${CMAKE_CURRENT_LIST_DIR}/sockets.cpp
 	${CMAKE_CURRENT_LIST_DIR}/system.cpp
 	${CMAKE_CURRENT_LIST_DIR}/timer.cpp
--- a/lib/irccd/cmd-server-cmode.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-cmode.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-cmode.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -44,7 +45,7 @@
 
 json::Value ServerChannelMode::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->cmode(
+	irccd.serverService().requireServer(request.at("server").toString())->cmode(
 		request.at("channel").toString(),
 		request.at("mode").toString()
 	);
--- a/lib/irccd/cmd-server-cnotice.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-cnotice.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-cnotice.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -44,7 +45,7 @@
 
 json::Value ServerChannelNotice::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->cnotice(
+	irccd.serverService().requireServer(request.at("server").toString())->cnotice(
 		request.at("channel").toString(),
 		request.at("message").toString()
 	);
--- a/lib/irccd/cmd-server-connect.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-connect.cpp	Tue May 10 22:51:10 2016 +0200
@@ -21,6 +21,7 @@
 #include "cmd-server-connect.hpp"
 #include "irccd.hpp"
 #include "server.hpp"
+#include "service-server.hpp"
 #include "util.hpp"
 
 namespace irccd {
@@ -156,11 +157,11 @@
 {
 	auto server = std::make_shared<Server>(readInfo(request), readIdentity(request), readSettings(request));
 
-	if (irccd.hasServer(server->info().name)) {
+	if (irccd.serverService().hasServer(server->info().name)) {
 		throw std::invalid_argument("server '" + server->info().name + "' already exists");
 	}
 
-	irccd.addServer(std::move(server));
+	irccd.serverService().addServer(std::move(server));
 
 	return RemoteCommand::exec(irccd, request);
 }
--- a/lib/irccd/cmd-server-disconnect.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-disconnect.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -43,9 +44,9 @@
 	auto it = request.find("server");
 
 	if (it == request.end()) {
-		irccd.clearServers();
+		irccd.serverService().clearServers();
 	} else {
-		irccd.removeServer(it->toString());
+		irccd.serverService().removeServer(it->toString());
 	}
 
 	return RemoteCommand::exec(irccd, request);
--- a/lib/irccd/cmd-server-info.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-info.cpp	Tue May 10 22:51:10 2016 +0200
@@ -20,6 +20,7 @@
 
 #include "cmd-server-info.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -51,7 +52,7 @@
 
 json::Value ServerInfo::exec(Irccd &irccd, const json::Value &request) const
 {
-	auto server = irccd.requireServer(request.at("server").toString());
+	auto server = irccd.serverService().requireServer(request.at("server").toString());
 	auto response = RemoteCommand::exec(irccd, request);
 
 	/* General stuff */
--- a/lib/irccd/cmd-server-invite.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-invite.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-invite.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -53,7 +54,7 @@
 
 json::Value ServerInvite::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(
+	irccd.serverService().requireServer(
 		request.at("server").toString())->invite(
 		request.at("target").toString(),
 		request.at("channel").toString()
--- a/lib/irccd/cmd-server-join.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-join.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-join.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -58,7 +59,7 @@
 
 json::Value ServerJoin::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(
+	irccd.serverService().requireServer(
 		request.at("server").toString())->join(
 		request.at("channel").toString(),
 		request.valueOr("password", json::Type::String, "").toString()
--- a/lib/irccd/cmd-server-kick.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-kick.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-kick.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -60,7 +61,7 @@
 
 json::Value ServerKick::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->kick(
+	irccd.serverService().requireServer(request.at("server").toString())->kick(
 		request.at("target").toString(),
 		request.at("channel").toString(),
 		request.valueOr("reason", json::Type::String, "").toString()
--- a/lib/irccd/cmd-server-list.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-list.cpp	Tue May 10 22:51:10 2016 +0200
@@ -20,6 +20,7 @@
 
 #include "cmd-server-list.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -40,7 +41,7 @@
 	auto json = json::object({});
 	auto list = json::array({});
 
-	for (const auto &server : irccd.servers()) {
+	for (const auto &server : irccd.serverService().servers()) {
 		list.append(server->info().name);
 	}
 
--- a/lib/irccd/cmd-server-me.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-me.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-me.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -53,7 +54,7 @@
 
 json::Value ServerMe::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->me(
+	irccd.serverService().requireServer(request.at("server").toString())->me(
 		request.at("target").toString(),
 		request.at("message").toString()
 	);
--- a/lib/irccd/cmd-server-message.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-message.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-message.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -53,7 +54,7 @@
 
 json::Value ServerMessage::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->me(
+	irccd.serverService().requireServer(request.at("server").toString())->me(
 		request.at("target").toString(),
 		request.at("message").toString()
 	);
--- a/lib/irccd/cmd-server-mode.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-mode.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-mode.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -51,7 +52,7 @@
 
 json::Value ServerMode::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->mode(request.at("mode").toString());
+	irccd.serverService().requireServer(request.at("server").toString())->mode(request.at("mode").toString());
 
 	return nullptr;
 }
--- a/lib/irccd/cmd-server-nick.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-nick.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-nick.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -51,7 +52,7 @@
 
 json::Value ServerNick::exec(Irccd &irccd, const json::Value &object) const
 {
-	irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString());
+	irccd.serverService().requireServer(object.at("server").toString())->nick(object.at("nickname").toString());
 
 	return nullptr;
 }
--- a/lib/irccd/cmd-server-notice.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-notice.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-notice.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -53,7 +54,7 @@
 
 json::Value ServerNotice::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->notice(
+	irccd.serverService().requireServer(request.at("server").toString())->notice(
 		request.at("target").toString(),
 		request.at("message").toString()
 	);
--- a/lib/irccd/cmd-server-part.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-part.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-part.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -58,7 +59,7 @@
 
 json::Value ServerPart::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->part(
+	irccd.serverService().requireServer(request.at("server").toString())->part(
 		request.at("channel").toString(),
 		request.valueOr("reason", "").toString()
 	);
--- a/lib/irccd/cmd-server-reconnect.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-reconnect.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -48,9 +49,9 @@
 	auto server = request.find("server");
 
 	if (server != request.end() && server->isString()) {
-		irccd.requireServer(server->toString())->reconnect();
+		irccd.serverService().requireServer(server->toString())->reconnect();
 	} else {
-		for (auto &server : irccd.servers()) {
+		for (auto &server : irccd.serverService().servers()) {
 			server->reconnect();
 		}
 	}
--- a/lib/irccd/cmd-server-topic.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/cmd-server-topic.cpp	Tue May 10 22:51:10 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-topic.hpp"
 #include "irccd.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -53,7 +54,7 @@
 
 json::Value ServerTopic::exec(Irccd &irccd, const json::Value &request) const
 {
-	irccd.requireServer(request.at("server").toString())->topic(
+	irccd.serverService().requireServer(request.at("server").toString())->topic(
 		request.at("channel").toString(),
 		request.at("topic").toString()
 	);
--- a/lib/irccd/irccd.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/irccd.cpp	Tue May 10 22:51:10 2016 +0200
@@ -25,8 +25,8 @@
 #include "irccd.hpp"
 #include "logger.hpp"
 #include "path.hpp"
-#include "server-event.hpp"
 #include "service-interrupt.hpp"
+#include "service-server.hpp"
 #include "util.hpp"
 
 using namespace std;
@@ -37,533 +37,6 @@
 
 namespace irccd {
 
-void Irccd::handleServerChannelMode(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string mode, std::string arg)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onChannelMode:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  mode: " << mode << "\n";
-	log::debug() << "  argument: " << arg << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onChannelMode"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "mode",	mode			},
-		{ "argument",	arg			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onChannelMode";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onChannelMode(std::move(server), std::move(origin), std::move(channel), std::move(mode), std::move(arg));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerChannelNotice(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onChannelNotice:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onChannelNotice"	},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "message",	message			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onChannelNotice";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onChannelNotice(std::move(server), std::move(origin), std::move(channel), std::move(message));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerConnect(std::weak_ptr<Server> ptr)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onConnect" << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onConnect"		},
-		{ "server",	server->info().name	}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, /* origin */ "", /* channel */ "",
-		[=] (Plugin &) -> std::string {
-			return "onConnect";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onConnect(std::move(server));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerInvite(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onInvite:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  target: " << target << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onInvite"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onInvite";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onInvite(std::move(server), std::move(origin), std::move(channel));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerJoin(std::weak_ptr<Server> ptr, std::string origin, std::string channel)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onJoin:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onJoin"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onJoin";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onJoin(std::move(server), std::move(origin), std::move(channel));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerKick(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target, std::string reason)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onKick:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  target: " << target << "\n";
-	log::debug() << "  reason: " << reason << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onKick"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "target",	target			},
-		{ "reason",	reason			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onKick";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onKick(std::move(server), std::move(origin), std::move(channel), std::move(target), std::move(reason));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerMessage(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onMessage:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onMessage"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "message",	message			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &plugin) -> std::string {
-			return util::parseMessage(message, server->settings().command, plugin.name()).second == util::MessageType::Command ? "onCommand" : "onMessage";
-		},
-		[=] (Plugin &plugin) {
-			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.name());
-
-			if (pack.second == util::MessageType::Command)
-				plugin.onCommand(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
-			else
-				plugin.onMessage(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerMe(std::weak_ptr<Server> ptr, std::string origin, std::string target, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onMe:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  target: " << target << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onMe"			},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "target",	target			},
-		{ "message",	message			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, target,
-		[=] (Plugin &) -> std::string {
-			return "onMe";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onMe(std::move(server), std::move(origin), std::move(target), std::move(message));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerMode(std::weak_ptr<Server> ptr, std::string origin, std::string mode)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onMode\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  mode: " << mode << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onMode"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "mode",	mode			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, /* channel */ "",
-		[=] (Plugin &) -> std::string {
-			return "onMode";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onMode(std::move(server), std::move(origin), std::move(mode));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerNames(std::weak_ptr<Server> ptr, std::string channel, std::set<std::string> nicknames)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onNames:\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  names: " << util::join(nicknames.begin(), nicknames.end(), ", ") << std::endl;
-
-	json::Value names(std::vector<json::Value>(nicknames.begin(), nicknames.end()));
-
-	broadcast(json::object({
-		{ "event",	"onNames"		},
-		{ "server",	server->info().name	},
-		{ "channel",	channel			},
-		{ "names",	std::move(names)	}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, /* origin */ "", channel,
-		[=] (Plugin &) -> std::string {
-			return "onNames";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onNames(std::move(server), std::move(channel), std::vector<std::string>(nicknames.begin(), nicknames.end()));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerNick(std::weak_ptr<Server> ptr, std::string origin, std::string nickname)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onNick:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  nickname: " << nickname << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onNick"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "nickname",	nickname		}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, /* channel */ "",
-		[=] (Plugin &) -> std::string {
-			return "onNick";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onNick(std::move(server), std::move(origin), std::move(nickname));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerNotice(std::weak_ptr<Server> ptr, std::string origin, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onNotice:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onNotice"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "message",	message			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, /* channel */ "",
-		[=] (Plugin &) -> std::string {
-			return "onNotice";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onNotice(std::move(server), std::move(origin), std::move(message));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerPart(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string reason)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onPart:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  reason: " << reason << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onPart"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "reason",	reason			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onPart";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onPart(std::move(server), std::move(origin), std::move(channel), std::move(reason));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerQuery(std::weak_ptr<Server> ptr, std::string origin, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onQuery:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onQuery"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "message",	message			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, /* channel */ "",
-		[=] (Plugin &plugin) -> std::string {
-			return util::parseMessage(message, server->settings().command, plugin.name()).second == util::MessageType::Command ? "onQueryCommand" : "onQuery";
-		},
-		[=] (Plugin &plugin) {
-			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.name());
-
-			if (pack.second == util::MessageType::Command)
-				plugin.onQueryCommand(std::move(server), std::move(origin), std::move(pack.first));
-			else
-				plugin.onQuery(std::move(server), std::move(origin), std::move(pack.first));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerTopic(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string topic)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onTopic:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  topic: " << topic << std::endl;
-
-	broadcast(json::object({
-		{ "event",	"onTopic"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "topic",	topic			}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, origin, channel,
-		[=] (Plugin &) -> std::string {
-			return "onTopic";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onTopic(std::move(server), std::move(origin), std::move(channel), std::move(topic));
-		}
-	));
-#endif
-}
-
-void Irccd::handleServerWhois(std::weak_ptr<Server> ptr, ServerWhois whois)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server) {
-		return;
-	}
-
-	log::debug() << "server " << server->info().name << ": event onWhois\n";
-	log::debug() << "  nickname: " << whois.nick << "\n";
-	log::debug() << "  username: " << whois.user << "\n";
-	log::debug() << "  host: " << whois.host << "\n";
-	log::debug() << "  realname: " << whois.realname << "\n";
-	log::debug() << "  channels: " << util::join(whois.channels.begin(), whois.channels.end()) << std::endl;
-
-	broadcast(json::object({
-		{ "server",	server->info().name	},
-		{ "nickname",	whois.nick		},
-		{ "username",	whois.user		},
-		{ "host",	whois.host		},
-		{ "realname",	whois.realname		}
-	}).toJson(0));
-
-#if defined(WITH_JS)
-	post(ServerEvent(server->info().name, /* origin */ "", /* channel */ "",
-		[=] (Plugin &) -> std::string {
-			return "onWhois";
-		},
-		[=] (Plugin &plugin) {
-			plugin.onWhois(std::move(server), std::move(whois));
-		}
-	));
-#endif
-}
-
 void Irccd::handleTransportCommand(std::weak_ptr<TransportClient> ptr, const json::Value &object)
 {
 	assert(object.isObject());
@@ -676,16 +149,9 @@
 	}
 }
 
-void Irccd::processServers(fd_set &input, fd_set &output)
-{
-	for (auto &server : m_servers) {
-		server->sync(input, output);
-	}
-}
-
 void Irccd::process(fd_set &setinput, fd_set &setoutput)
 {
-	// TODO: create services for servers and transports
+	// TODO: create services for transports
 	for (const auto &service : m_services) {
 		service->sync(setinput, setoutput);
 	}
@@ -695,15 +161,14 @@
 
 	/* 3. Check for transport servers */
 	processTransportServers(setinput);
-
-	/* 4. Check for servers */
-	processServers(setinput, setoutput);
 }
 
 Irccd::Irccd()
 	: m_interruptService(std::make_shared<InterruptService>())
+	, m_serverService(std::make_shared<ServerService>(*this))
 {
 	m_services.push_back(m_interruptService);
+	m_services.push_back(m_serverService);
 }
 
 void Irccd::post(std::function<void (Irccd &)> ev) noexcept
@@ -714,89 +179,6 @@
 	m_interruptService->interrupt();
 }
 
-void Irccd::addServer(shared_ptr<Server> server) noexcept
-{
-#if 0
-	assert(m_servers.count(server->info().name) == 0);
-#endif
-
-	std::weak_ptr<Server> ptr(server);
-
-	server->onChannelMode.connect(std::bind(&Irccd::handleServerChannelMode, this, ptr, _1, _2, _3, _4));
-	server->onChannelNotice.connect(std::bind(&Irccd::handleServerChannelNotice, this, ptr, _1, _2, _3));
-	server->onConnect.connect(std::bind(&Irccd::handleServerConnect, this, ptr));
-	server->onInvite.connect(std::bind(&Irccd::handleServerInvite, this, ptr, _1, _2, _3));
-	server->onJoin.connect(std::bind(&Irccd::handleServerJoin, this, ptr, _1, _2));
-	server->onKick.connect(std::bind(&Irccd::handleServerKick, this, ptr, _1, _2, _3, _4));
-	server->onMessage.connect(std::bind(&Irccd::handleServerMessage, this, ptr, _1, _2, _3));
-	server->onMe.connect(std::bind(&Irccd::handleServerMe, this, ptr, _1, _2, _3));
-	server->onMode.connect(std::bind(&Irccd::handleServerMode, this, ptr, _1, _2));
-	server->onNames.connect(std::bind(&Irccd::handleServerNames, this, ptr, _1, _2));
-	server->onNick.connect(std::bind(&Irccd::handleServerNick, this, ptr, _1, _2));
-	server->onNotice.connect(std::bind(&Irccd::handleServerNotice, this, ptr, _1, _2));
-	server->onPart.connect(std::bind(&Irccd::handleServerPart, this, ptr, _1, _2, _3));
-	server->onQuery.connect(std::bind(&Irccd::handleServerQuery, this, ptr, _1, _2));
-	server->onTopic.connect(std::bind(&Irccd::handleServerTopic, this, ptr, _1, _2, _3));
-	server->onWhois.connect(std::bind(&Irccd::handleServerWhois, this, ptr, _1));
-	server->onDie.connect([this, ptr] () {
-		post([=] (Irccd &) {
-			auto server = ptr.lock();
-
-			if (server) {
-				log::info(fmt::format("server {}: removed", server->info().name));
-				m_servers.erase(std::find(m_servers.begin(), m_servers.end(), server));
-			}
-		});
-	});
-
-	m_servers.push_back(std::move(server));
-}
-
-std::shared_ptr<Server> Irccd::getServer(const std::string &name) const noexcept
-{
-	auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
-		return server->info().name == name;
-	});
-
-	if (it == m_servers.end()) {
-		return nullptr;
-	}
-
-	return *it;
-}
-
-std::shared_ptr<Server> Irccd::requireServer(const std::string &name) const
-{
-	auto server = getServer(name);
-
-	if (!server) {
-		throw std::invalid_argument("server {} not found"_format(name));
-	}
-
-	return server;
-}
-
-void Irccd::removeServer(const std::string &name)
-{
-	auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
-		return server->info().name == name;
-	});
-
-	if (it != m_servers.end()) {
-		(*it)->disconnect();
-		m_servers.erase(it);
-	}
-}
-
-void Irccd::clearServers() noexcept
-{
-	for (auto &server : m_servers) {
-		server->disconnect();
-	}
-
-	m_servers.clear();
-}
-
 void Irccd::addTransport(std::shared_ptr<TransportServer> ts)
 {
 	m_transportServers.push_back(std::move(ts));
@@ -991,17 +373,11 @@
 	FD_ZERO(&setinput);
 	FD_ZERO(&setoutput);
 
-	// TODO: create services for servers and transports
+	// TODO: create services for transports
 	for (const auto &service : m_services) {
 		service->prepare(setinput, setoutput, max);
 	}
 
-	/* 2. Add servers */
-	for (auto &server : m_servers) {
-		server->update();
-		server->prepare(setinput, setoutput, max);
-	}
-
 	/* 3. Add transports clients */
 	for (auto &client : m_transportClients) {
 		set(setinput, client->handle());
--- a/lib/irccd/irccd.hpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/irccd.hpp	Tue May 10 22:51:10 2016 +0200
@@ -51,6 +51,7 @@
 class InterruptService;
 class Irccd;
 class Plugin;
+class ServerService;
 class Service;
 class TransportCommand;
 
@@ -75,9 +76,6 @@
 	std::mutex m_mutex;
 	std::vector<std::function<void (Irccd &)>> m_events;
 
-	// Servers.
-	std::vector<std::shared_ptr<Server>> m_servers;
-
 	// Optional plugins.
 #if defined(WITH_JS)
 	std::vector<std::shared_ptr<Plugin>> m_plugins;
@@ -92,31 +90,10 @@
 
 	// Services
 	std::shared_ptr<InterruptService> m_interruptService;
+	std::shared_ptr<ServerService> m_serverService;
 	std::vector<std::shared_ptr<Service>> m_services;
 
 	/*
-	 * Server slots
-	 * ----------------------------------------------------------
-	 */
-
-	void handleServerChannelMode(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg);
-	void handleServerChannelNotice(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string notice);
-	void handleServerConnect(std::weak_ptr<Server> server);
-	void handleServerInvite(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target);
-	void handleServerJoin(std::weak_ptr<Server> server, std::string origin, std::string channel);
-	void handleServerKick(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason);
-	void handleServerMessage(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string message);
-	void handleServerMe(std::weak_ptr<Server> server, std::string origin, std::string target, std::string message);
-	void handleServerMode(std::weak_ptr<Server> server, std::string origin, std::string mode);
-	void handleServerNames(std::weak_ptr<Server> server, std::string channel, std::set<std::string> nicknames);
-	void handleServerNick(std::weak_ptr<Server> server, std::string origin, std::string nickname);
-	void handleServerNotice(std::weak_ptr<Server> server, std::string origin, std::string message);
-	void handleServerPart(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string reason);
-	void handleServerQuery(std::weak_ptr<Server> server, std::string origin, std::string message);
-	void handleServerTopic(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string topic);
-	void handleServerWhois(std::weak_ptr<Server> server, ServerWhois whois);
-
-	/*
 	 * Transport clients slots
 	 * ----------------------------------------------------------
 	 */
@@ -144,9 +121,14 @@
 
 	void processTransportClients(fd_set &input, fd_set &output);
 	void processTransportServers(fd_set &input);
-	void processServers(fd_set &input, fd_set &output);
 	void process(fd_set &setinput, fd_set &setoutput);
 
+	Irccd(const Irccd &) = delete;
+	Irccd(Irccd &&) = delete;
+
+	Irccd &operator=(const Irccd &) = delete;
+	Irccd &operator=(Irccd &&) = delete;
+
 public:
 	/**
 	 * Prepare standard services.
@@ -162,6 +144,16 @@
 	}
 
 	/**
+	 * Access the server service.
+	 *
+	 * \return the service
+	 */
+	inline ServerService &serverService() noexcept
+	{
+		return *m_serverService;
+	}
+
+	/**
 	 * Add an event to the queue. This will immediately signals the event loop to interrupt itself to dispatch
 	 * the pending events.
 	 *
@@ -171,79 +163,6 @@
 	void post(std::function<void (Irccd &)> ev) noexcept;
 
 	/*
-	 * Server management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions to get or create new servers.
-	 *
-	 * Servers that are added to this instance are automatically polled when run() is called.
-	 */
-
-	/**
-	 * Check if a server exists.
-	 *
-	 * \param name the name
-	 * \return true if exists
-	 */
-	inline bool hasServer(const std::string &name) const noexcept
-	{
-		return std::count_if(m_servers.cbegin(), m_servers.end(), [&] (const auto &sv) {
-			return sv->info().name == name;
-		}) > 0;
-	}
-
-	/**
-	 * Add a new server to the application.
-	 *
-	 * \pre hasServer must return false
-	 * \param sv the server
-	 */
-	void addServer(std::shared_ptr<Server> sv) noexcept;
-
-	/**
-	 * Get a server or empty one if not found
-	 *
-	 * \param name the server name
-	 * \return the server or empty one if not found
-	 */
-	std::shared_ptr<Server> getServer(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
-	 */
-	std::shared_ptr<Server> requireServer(const std::string &name) const;
-
-	/**
-	 * Get the list of servers
-	 *
-	 * \return the servers
-	 */
-	inline const std::vector<std::shared_ptr<Server>> &servers() const noexcept
-	{
-		return m_servers;
-	}
-
-	/**
-	 * Remove a server from the irccd instance.
-	 *
-	 * The server if any, will be disconnected.
-	 *
-	 * \param name the server name
-	 */
-	void removeServer(const std::string &name);
-
-	/**
-	 * Remove all servers.
-	 *
-	 * All servers will be disconnected.
-	 */
-	void clearServers() noexcept;
-
-	/*
 	 * Transport management
 	 * ----------------------------------------------------------
 	 *
--- a/lib/irccd/js-server.cpp	Tue May 10 22:11:36 2016 +0200
+++ b/lib/irccd/js-server.cpp	Tue May 10 22:51:10 2016 +0200
@@ -22,6 +22,7 @@
 #include "irccd.hpp"
 #include "js-server.hpp"
 #include "server.hpp"
+#include "service-server.hpp"
 
 namespace irccd {
 
@@ -424,7 +425,7 @@
 	auto server = duk::get<duk::Shared<Server>>(ctx, 0);
 
 	if (server) {
-		duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->addServer(server);
+		duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().addServer(server);
 	}
 
 	return 0;
@@ -447,7 +448,7 @@
 	const auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd");
 
 	try {
-		duk::push(ctx, duk::Shared<Server>{irccd->requireServer(name)});
+		duk::push(ctx, duk::Shared<Server>{irccd->serverService().requireServer(name)});
 	} catch (...) {
 		return 0;
 	}
@@ -468,7 +469,7 @@
 {
 	duk::push(ctx, duk::Object{});
 
-	for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->servers()) {
+	for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers()) {
 		duk::putProperty(ctx, -1, server->info().name, duk::Shared<Server>{server});
 	}
 
@@ -486,7 +487,7 @@
  */
 duk::Ret remove(duk::ContextPtr ctx)
 {
-	duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->removeServer(duk::require<std::string>(ctx, 0));
+	duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().removeServer(duk::require<std::string>(ctx, 0));
 
 	return 0;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/service-server.cpp	Tue May 10 22:51:10 2016 +0200
@@ -0,0 +1,671 @@
+/*
+ * 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 <format.h>
+
+#include "irccd.hpp"
+#include "logger.hpp"
+#include "server-event.hpp"
+#include "service-server.hpp"
+#include "sysconfig.hpp"
+#include "util.hpp"
+
+using namespace fmt::literals;
+
+namespace irccd {
+
+void ServerService::handleChannelMode(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string mode, std::string arg)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onChannelMode:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  mode: " << mode << "\n";
+	log::debug() << "  argument: " << arg << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onChannelMode"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "mode",	mode			},
+		{ "argument",	arg			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onChannelMode";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onChannelMode(std::move(server), std::move(origin), std::move(channel), std::move(mode), std::move(arg));
+		}
+	));
+#endif
+}
+
+void ServerService::handleChannelNotice(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onChannelNotice:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onChannelNotice"	},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "message",	message			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onChannelNotice";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onChannelNotice(std::move(server), std::move(origin), std::move(channel), std::move(message));
+		}
+	));
+#endif
+}
+
+void ServerService::handleConnect(std::weak_ptr<Server> ptr)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onConnect" << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onConnect"		},
+		{ "server",	server->info().name	}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, /* origin */ "", /* channel */ "",
+		[=] (Plugin &) -> std::string {
+			return "onConnect";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onConnect(std::move(server));
+		}
+	));
+#endif
+}
+
+void ServerService::handleInvite(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onInvite:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  target: " << target << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onInvite"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onInvite";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onInvite(std::move(server), std::move(origin), std::move(channel));
+		}
+	));
+#endif
+}
+
+void ServerService::handleJoin(std::weak_ptr<Server> ptr, std::string origin, std::string channel)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onJoin:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onJoin"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onJoin";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onJoin(std::move(server), std::move(origin), std::move(channel));
+		}
+	));
+#endif
+}
+
+void ServerService::handleKick(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target, std::string reason)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onKick:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  target: " << target << "\n";
+	log::debug() << "  reason: " << reason << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onKick"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "target",	target			},
+		{ "reason",	reason			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onKick";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onKick(std::move(server), std::move(origin), std::move(channel), std::move(target), std::move(reason));
+		}
+	));
+#endif
+}
+
+void ServerService::handleMessage(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onMessage:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onMessage"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "message",	message			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &plugin) -> std::string {
+			return util::parseMessage(message, server->settings().command, plugin.name()).second == util::MessageType::Command ? "onCommand" : "onMessage";
+		},
+		[=] (Plugin &plugin) {
+			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.name());
+
+			if (pack.second == util::MessageType::Command)
+				plugin.onCommand(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
+			else
+				plugin.onMessage(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
+		}
+	));
+#endif
+}
+
+void ServerService::handleMe(std::weak_ptr<Server> ptr, std::string origin, std::string target, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onMe:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  target: " << target << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onMe"			},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "target",	target			},
+		{ "message",	message			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, target,
+		[=] (Plugin &) -> std::string {
+			return "onMe";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onMe(std::move(server), std::move(origin), std::move(target), std::move(message));
+		}
+	));
+#endif
+}
+
+void ServerService::handleMode(std::weak_ptr<Server> ptr, std::string origin, std::string mode)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onMode\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  mode: " << mode << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onMode"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "mode",	mode			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, /* channel */ "",
+		[=] (Plugin &) -> std::string {
+			return "onMode";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onMode(std::move(server), std::move(origin), std::move(mode));
+		}
+	));
+#endif
+}
+
+void ServerService::handleNames(std::weak_ptr<Server> ptr, std::string channel, std::set<std::string> nicknames)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onNames:\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  names: " << util::join(nicknames.begin(), nicknames.end(), ", ") << std::endl;
+
+	json::Value names(std::vector<json::Value>(nicknames.begin(), nicknames.end()));
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onNames"		},
+		{ "server",	server->info().name	},
+		{ "channel",	channel			},
+		{ "names",	std::move(names)	}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, /* origin */ "", channel,
+		[=] (Plugin &) -> std::string {
+			return "onNames";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onNames(std::move(server), std::move(channel), std::vector<std::string>(nicknames.begin(), nicknames.end()));
+		}
+	));
+#endif
+}
+
+void ServerService::handleNick(std::weak_ptr<Server> ptr, std::string origin, std::string nickname)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onNick:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  nickname: " << nickname << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onNick"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "nickname",	nickname		}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, /* channel */ "",
+		[=] (Plugin &) -> std::string {
+			return "onNick";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onNick(std::move(server), std::move(origin), std::move(nickname));
+		}
+	));
+#endif
+}
+
+void ServerService::handleNotice(std::weak_ptr<Server> ptr, std::string origin, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onNotice:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onNotice"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "message",	message			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, /* channel */ "",
+		[=] (Plugin &) -> std::string {
+			return "onNotice";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onNotice(std::move(server), std::move(origin), std::move(message));
+		}
+	));
+#endif
+}
+
+void ServerService::handlePart(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string reason)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onPart:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  reason: " << reason << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onPart"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "reason",	reason			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onPart";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onPart(std::move(server), std::move(origin), std::move(channel), std::move(reason));
+		}
+	));
+#endif
+}
+
+void ServerService::handleQuery(std::weak_ptr<Server> ptr, std::string origin, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onQuery:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onQuery"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "message",	message			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, /* channel */ "",
+		[=] (Plugin &plugin) -> std::string {
+			return util::parseMessage(message, server->settings().command, plugin.name()).second == util::MessageType::Command ? "onQueryCommand" : "onQuery";
+		},
+		[=] (Plugin &plugin) {
+			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.name());
+
+			if (pack.second == util::MessageType::Command)
+				plugin.onQueryCommand(std::move(server), std::move(origin), std::move(pack.first));
+			else
+				plugin.onQuery(std::move(server), std::move(origin), std::move(pack.first));
+		}
+	));
+#endif
+}
+
+void ServerService::handleTopic(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string topic)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onTopic:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  topic: " << topic << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "event",	"onTopic"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "topic",	topic			}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, origin, channel,
+		[=] (Plugin &) -> std::string {
+			return "onTopic";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onTopic(std::move(server), std::move(origin), std::move(channel), std::move(topic));
+		}
+	));
+#endif
+}
+
+void ServerService::handleWhois(std::weak_ptr<Server> ptr, ServerWhois whois)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server) {
+		return;
+	}
+
+	log::debug() << "server " << server->info().name << ": event onWhois\n";
+	log::debug() << "  nickname: " << whois.nick << "\n";
+	log::debug() << "  username: " << whois.user << "\n";
+	log::debug() << "  host: " << whois.host << "\n";
+	log::debug() << "  realname: " << whois.realname << "\n";
+	log::debug() << "  channels: " << util::join(whois.channels.begin(), whois.channels.end()) << std::endl;
+
+	m_irccd.broadcast(json::object({
+		{ "server",	server->info().name	},
+		{ "nickname",	whois.nick		},
+		{ "username",	whois.user		},
+		{ "host",	whois.host		},
+		{ "realname",	whois.realname		}
+	}).toJson(0));
+
+#if defined(WITH_JS)
+	m_irccd.post(ServerEvent(server->info().name, /* origin */ "", /* channel */ "",
+		[=] (Plugin &) -> std::string {
+			return "onWhois";
+		},
+		[=] (Plugin &plugin) {
+			plugin.onWhois(std::move(server), std::move(whois));
+		}
+	));
+#endif
+}
+
+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::hasServer(const std::string &name) const noexcept
+{
+	return std::count_if(m_servers.cbegin(), m_servers.end(), [&] (const auto &sv) {
+		return sv->info().name == name;
+	}) > 0;
+}
+
+void ServerService::addServer(std::shared_ptr<Server> server) noexcept
+{
+	assert(!hasServer(server->info().name));
+
+	using namespace std::placeholders;
+
+	std::weak_ptr<Server> ptr(server);
+
+	server->onChannelMode.connect(std::bind(&ServerService::handleChannelMode, this, ptr, _1, _2, _3, _4));
+	server->onChannelNotice.connect(std::bind(&ServerService::handleChannelNotice, this, ptr, _1, _2, _3));
+	server->onConnect.connect(std::bind(&ServerService::handleConnect, this, ptr));
+	server->onInvite.connect(std::bind(&ServerService::handleInvite, this, ptr, _1, _2, _3));
+	server->onJoin.connect(std::bind(&ServerService::handleJoin, this, ptr, _1, _2));
+	server->onKick.connect(std::bind(&ServerService::handleKick, this, ptr, _1, _2, _3, _4));
+	server->onMessage.connect(std::bind(&ServerService::handleMessage, this, ptr, _1, _2, _3));
+	server->onMe.connect(std::bind(&ServerService::handleMe, this, ptr, _1, _2, _3));
+	server->onMode.connect(std::bind(&ServerService::handleMode, this, ptr, _1, _2));
+	server->onNames.connect(std::bind(&ServerService::handleNames, this, ptr, _1, _2));
+	server->onNick.connect(std::bind(&ServerService::handleNick, this, ptr, _1, _2));
+	server->onNotice.connect(std::bind(&ServerService::handleNotice, this, ptr, _1, _2));
+	server->onPart.connect(std::bind(&ServerService::handlePart, this, ptr, _1, _2, _3));
+	server->onQuery.connect(std::bind(&ServerService::handleQuery, this, ptr, _1, _2));
+	server->onTopic.connect(std::bind(&ServerService::handleTopic, this, ptr, _1, _2, _3));
+	server->onWhois.connect(std::bind(&ServerService::handleWhois, this, ptr, _1));
+	server->onDie.connect([this, ptr] () {
+		m_irccd.post([=] (Irccd &) {
+			auto server = ptr.lock();
+
+			if (server) {
+				log::info("server {}: removed"_format(server->info().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::getServer(const std::string &name) const noexcept
+{
+	auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
+		return server->info().name == name;
+	});
+
+	if (it == m_servers.end()) {
+		return nullptr;
+	}
+
+	return *it;
+}
+
+std::shared_ptr<Server> ServerService::requireServer(const std::string &name) const
+{
+	auto server = getServer(name);
+
+	if (!server) {
+		throw std::invalid_argument("server {} not found"_format(name));
+	}
+
+	return server;
+}
+
+void ServerService::removeServer(const std::string &name)
+{
+	auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
+		return server->info().name == name;
+	});
+
+	if (it != m_servers.end()) {
+		(*it)->disconnect();
+		m_servers.erase(it);
+	}
+}
+
+void ServerService::clearServers() noexcept
+{
+	for (auto &server : m_servers) {
+		server->disconnect();
+	}
+
+	m_servers.clear();
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/service-server.hpp	Tue May 10 22:51:10 2016 +0200
@@ -0,0 +1,142 @@
+/*
+ * 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 "service.hpp"
+
+namespace irccd {
+
+class Irccd;
+class Server;
+class ServerWhois;
+
+/**
+ * \brief Manage IRC servers.
+ */
+class ServerService : public Service {
+private:
+	Irccd &m_irccd;
+	std::vector<std::shared_ptr<Server>> m_servers;
+
+	void handleChannelMode(std::weak_ptr<Server>, std::string, std::string, std::string, std::string);
+	void handleChannelNotice(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleConnect(std::weak_ptr<Server>);
+	void handleInvite(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleJoin(std::weak_ptr<Server>, std::string, std::string);
+	void handleKick(std::weak_ptr<Server>, std::string, std::string, std::string, std::string);
+	void handleMessage(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleMe(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleMode(std::weak_ptr<Server>, std::string, std::string);
+	void handleNames(std::weak_ptr<Server>, std::string, std::set<std::string>);
+	void handleNick(std::weak_ptr<Server>, std::string, std::string);
+	void handleNotice(std::weak_ptr<Server>, std::string, std::string);
+	void handlePart(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleQuery(std::weak_ptr<Server>, std::string, std::string);
+	void handleTopic(std::weak_ptr<Server>, std::string, std::string, std::string);
+	void handleWhois(std::weak_ptr<Server>, ServerWhois);
+
+public:
+	/**
+	 * Create the server service.
+	 */
+	ServerService(Irccd &instance);
+
+	/**
+	 * \copydoc Service::prepare
+	 */
+	void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
+
+	/**
+	 * \copydoc Service::sync
+	 */
+	void sync(fd_set &in, fd_set &out) override;
+
+	/**
+	 * Check if a server exists.
+	 *
+	 * \param name the name
+	 * \return true if exists
+	 */
+	bool hasServer(const std::string &name) const noexcept;
+
+	/**
+	 * Add a new server to the application.
+	 *
+	 * \pre hasServer must return false
+	 * \param sv the server
+	 */
+	void addServer(std::shared_ptr<Server> sv) noexcept;
+
+	/**
+	 * Get a server or empty one if not found
+	 *
+	 * \param name the server name
+	 * \return the server or empty one if not found
+	 */
+	std::shared_ptr<Server> getServer(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
+	 */
+	std::shared_ptr<Server> requireServer(const std::string &name) const;
+
+	/**
+	 * Get the list of servers
+	 *
+	 * \return the servers
+	 */
+	inline const std::vector<std::shared_ptr<Server>> &servers() const noexcept
+	{
+		return m_servers;
+	}
+
+	/**
+	 * Remove a server from the irccd instance.
+	 *
+	 * The server if any, will be disconnected.
+	 *
+	 * \param name the server name
+	 */
+	void removeServer(const std::string &name);
+
+	/**
+	 * Remove all servers.
+	 *
+	 * All servers will be disconnected.
+	 */
+	void clearServers() noexcept;
+};
+
+} // !irccd
+
+#endif // !IRCCD_SERVICE_SERVER_HPP