changeset 75:f8160d515a76

Irccd: rework a lot the JavaScript library
author David Demelier <markand@malikania.fr>
date Wed, 30 Mar 2016 13:52:47 +0200
parents 35ef15100de8
children 7e9a1faeb6f6
files cmake/IrccdOptions.cmake cmake/internal/Irccd2Config.cmake.in lib/CMakeLists.txt lib/irccd/CMakeSources.cmake lib/irccd/application.cpp lib/irccd/application.h lib/irccd/cmd-help.cpp lib/irccd/cmd-help.h lib/irccd/cmd-plugin-info.cpp lib/irccd/cmd-plugin-info.h lib/irccd/cmd-plugin-list.cpp lib/irccd/cmd-plugin-list.h lib/irccd/cmd-plugin-load.cpp lib/irccd/cmd-plugin-load.h lib/irccd/cmd-plugin-reload.cpp lib/irccd/cmd-plugin-reload.h lib/irccd/cmd-plugin-unload.cpp lib/irccd/cmd-plugin-unload.h lib/irccd/cmd-server-cmode.cpp lib/irccd/cmd-server-cmode.h lib/irccd/cmd-server-cnotice.cpp lib/irccd/cmd-server-cnotice.h lib/irccd/cmd-server-connect.cpp lib/irccd/cmd-server-connect.h lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-disconnect.h lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-info.h lib/irccd/cmd-server-invite.cpp lib/irccd/cmd-server-invite.h lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-join.h lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-kick.h lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-list.h lib/irccd/cmd-server-me.cpp lib/irccd/cmd-server-me.h lib/irccd/cmd-server-message.cpp lib/irccd/cmd-server-message.h lib/irccd/cmd-server-mode.cpp lib/irccd/cmd-server-mode.h lib/irccd/cmd-server-nick.cpp lib/irccd/cmd-server-nick.h lib/irccd/cmd-server-notice.cpp lib/irccd/cmd-server-notice.h lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-part.h lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-reconnect.h lib/irccd/cmd-server-topic.cpp lib/irccd/cmd-server-topic.h lib/irccd/cmd-watch.cpp lib/irccd/cmd-watch.h lib/irccd/command.cpp lib/irccd/command.h lib/irccd/command/CMakeSources.cmake lib/irccd/command/command.cpp lib/irccd/command/command.h lib/irccd/command/help.cpp lib/irccd/command/help.h lib/irccd/command/plugin-info.cpp lib/irccd/command/plugin-info.h lib/irccd/command/plugin-list.cpp lib/irccd/command/plugin-list.h lib/irccd/command/plugin-load.cpp lib/irccd/command/plugin-load.h lib/irccd/command/plugin-reload.cpp lib/irccd/command/plugin-reload.h lib/irccd/command/plugin-unload.cpp lib/irccd/command/plugin-unload.h lib/irccd/command/server-cmode.cpp lib/irccd/command/server-cmode.h lib/irccd/command/server-cnotice.cpp lib/irccd/command/server-cnotice.h lib/irccd/command/server-connect.cpp lib/irccd/command/server-connect.h lib/irccd/command/server-disconnect.cpp lib/irccd/command/server-disconnect.h lib/irccd/command/server-info.cpp lib/irccd/command/server-info.h lib/irccd/command/server-invite.cpp lib/irccd/command/server-invite.h lib/irccd/command/server-join.cpp lib/irccd/command/server-join.h lib/irccd/command/server-kick.cpp lib/irccd/command/server-kick.h lib/irccd/command/server-list.cpp lib/irccd/command/server-list.h lib/irccd/command/server-me.cpp lib/irccd/command/server-me.h lib/irccd/command/server-message.cpp lib/irccd/command/server-message.h lib/irccd/command/server-mode.cpp lib/irccd/command/server-mode.h lib/irccd/command/server-nick.cpp lib/irccd/command/server-nick.h lib/irccd/command/server-notice.cpp lib/irccd/command/server-notice.h lib/irccd/command/server-part.cpp lib/irccd/command/server-part.h lib/irccd/command/server-reconnect.cpp lib/irccd/command/server-reconnect.h lib/irccd/command/server-topic.cpp lib/irccd/command/server-topic.h lib/irccd/command/watch.cpp lib/irccd/command/watch.h lib/irccd/config.cpp lib/irccd/connection.cpp lib/irccd/connection.h lib/irccd/directory.cpp lib/irccd/directory.h lib/irccd/elapsed-timer.cpp lib/irccd/elapsed-timer.h lib/irccd/filesystem.cpp lib/irccd/filesystem.h lib/irccd/ini.cpp lib/irccd/ini.h lib/irccd/irccd.cpp lib/irccd/irccd.h lib/irccd/irccdctl.cpp lib/irccd/irccdctl.h lib/irccd/js-directory.cpp lib/irccd/js-directory.h lib/irccd/js-elapsed-timer.cpp lib/irccd/js-elapsed-timer.h lib/irccd/js-file.cpp lib/irccd/js-file.h lib/irccd/js-irccd.cpp lib/irccd/js-irccd.h lib/irccd/js-logger.cpp lib/irccd/js-logger.h lib/irccd/js-plugin.cpp lib/irccd/js-plugin.h lib/irccd/js-server.cpp lib/irccd/js-server.h lib/irccd/js-system.cpp lib/irccd/js-system.h lib/irccd/js-timer.cpp lib/irccd/js-timer.h lib/irccd/js-unicode.cpp lib/irccd/js-unicode.h lib/irccd/js-util.cpp lib/irccd/js-util.h lib/irccd/js.h lib/irccd/js/CMakeSources.cmake lib/irccd/js/directory.cpp lib/irccd/js/directory.h lib/irccd/js/elapsed-timer.cpp lib/irccd/js/elapsed-timer.h lib/irccd/js/file.cpp lib/irccd/js/file.h lib/irccd/js/irccd.cpp lib/irccd/js/irccd.h lib/irccd/js/js.cpp lib/irccd/js/js.h lib/irccd/js/logger.cpp lib/irccd/js/logger.h lib/irccd/js/plugin.cpp lib/irccd/js/plugin.h lib/irccd/js/server.cpp lib/irccd/js/server.h lib/irccd/js/system.cpp lib/irccd/js/system.h lib/irccd/js/timer.cpp lib/irccd/js/timer.h lib/irccd/js/unicode.cpp lib/irccd/js/unicode.h lib/irccd/js/util.cpp lib/irccd/js/util.h lib/irccd/path.cpp lib/irccd/plugin.cpp lib/irccd/plugin.h lib/irccd/private/CMakeSources.cmake lib/irccd/private/connection.cpp lib/irccd/private/connection.h lib/irccd/private/directory.cpp lib/irccd/private/directory.h lib/irccd/private/elapsed-timer.cpp lib/irccd/private/elapsed-timer.h lib/irccd/private/filesystem.cpp lib/irccd/private/filesystem.h lib/irccd/private/ini.cpp lib/irccd/private/ini.h lib/irccd/private/signals.h lib/irccd/private/sockets.cpp lib/irccd/private/sockets.h lib/irccd/private/xdg.cpp lib/irccd/private/xdg.h lib/irccd/server-state.h lib/irccd/server.cpp lib/irccd/server.h lib/irccd/signals.h lib/irccd/sockets.cpp lib/irccd/sockets.h lib/irccd/system.cpp lib/irccd/timer.h lib/irccd/transport-client.h lib/irccd/transport-server.h lib/irccd/xdg.cpp lib/irccd/xdg.h tests/elapsedtimer/CMakeLists.txt tests/elapsedtimer/main.cpp tests/js-elapsedtimer/CMakeLists.txt tests/js-elapsedtimer/main.cpp tests/js-file/CMakeLists.txt tests/js-file/main.cpp tests/js-irccd/CMakeLists.txt tests/js-irccd/main.cpp tests/js-logger/CMakeLists.txt tests/js-logger/main.cpp tests/js-system/CMakeLists.txt tests/js-system/main.cpp tests/js-timer/CMakeLists.txt tests/js-timer/main.cpp tests/js-unicode/CMakeLists.txt tests/js-unicode/main.cpp tests/js-util/CMakeLists.txt tests/js-util/main.cpp tests/path/CMakeLists.txt tests/path/main.cpp tests/rules/CMakeLists.txt tests/rules/main.cpp tests/timer/CMakeLists.txt tests/timer/main.cpp tests/util/CMakeLists.txt tests/util/main.cpp
diffstat 227 files changed, 17473 insertions(+), 17371 deletions(-) [+]
line wrap: on
line diff
--- a/cmake/IrccdOptions.cmake	Sat Mar 26 14:41:53 2016 +0100
+++ b/cmake/IrccdOptions.cmake	Wed Mar 30 13:52:47 2016 +0200
@@ -109,6 +109,7 @@
 set(WITH_BINDIR "bin" CACHE STRING "Binary directory")
 set(WITH_MANDIR "share/man" CACHE STRING "Man directory")
 set(WITH_CONFDIR "etc" CACHE STRING "Configuration directory")
+set(WITH_CMAKEDIR "lib/cmake" CACHE STRING "Directory for CMake modules")
 set(WITH_PKGCONFIGDIR "lib/pkgconfig" CACHE STRING "Directory for pkg-config file")
 
 #
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/internal/Irccd2Config.cmake.in	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/Irccd2Targets.cmake")
--- a/lib/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -25,9 +25,12 @@
 target_include_directories(
 	libirccd
 	PUBLIC
-		${irccd_BINARY_DIR}
+		$<BUILD_INTERFACE:${duktape_SOURCE_DIR}>
+		$<BUILD_INTERFACE:${extern-libircclient_SOURCE_DIR}/include>
+		$<BUILD_INTERFACE:${irccd_BINARY_DIR}>
+		$<BUILD_INTERFACE:${lib_SOURCE_DIR}>
+		$<INSTALL_INTERFACE:include>
 		${OPENSSL_INCLUDE_DIR}
-		${lib_SOURCE_DIR}
 )
 
 if (IRCCD_SYSTEM_WINDOWS)
@@ -36,7 +39,8 @@
 	list(APPEND LIBRARIES resolv)
 endif ()
 
-target_link_libraries(libirccd extern-duktape extern-ircclient extern-jansson ${LIBRARIES})
+target_link_libraries(libirccd PRIVATE extern-duktape extern-ircclient extern-jansson)
+target_link_libraries(libirccd ${LIBRARIES})
 
 set_target_properties(
 	libirccd
@@ -49,11 +53,50 @@
 
 install(
 	TARGETS libirccd
+	EXPORT Irccd2Targets
 	RUNTIME DESTINATION bin
 	LIBRARY DESTINATION lib
 	ARCHIVE DESTINATION lib
 )
 
+# CMake target exports.
+export(
+	EXPORT Irccd2Targets
+	FILE "${CMAKE_BINARY_DIR}/Irccd2Targets.cmake"
+	NAMESPACE Irccd2::
+)
+
+# CMake version file.
+include(CMakePackageConfigHelpers)
+
+write_basic_package_version_file(
+	"${CMAKE_CURRENT_BINARY_DIR}/Irccd2ConfigVersion.cmake"
+	VERSION ${IRCCD_VERSION}
+	COMPATIBILITY AnyNewerVersion
+)
+
+# Irccd main module file.
+configure_file(
+	"${CMAKE_SOURCE_DIR}/cmake/internal/Irccd2Config.cmake.in"
+	"${CMAKE_BINARY_DIR}/Irccd2Config.cmake"
+	COPYONLY
+)
+
+# Install all CMake stuff.
+install(
+	EXPORT Irccd2Targets
+	FILE Irccd2Targets.cmake
+	NAMESPACE Irccd2::
+	DESTINATION ${WITH_CMAKEDIR}/Irccd2
+)
+
+install(
+	FILES
+		"${CMAKE_BINARY_DIR}/Irccd2Config.cmake"
+		"${CMAKE_BINARY_DIR}/Irccd2ConfigVersion.cmake"
+	DESTINATION ${WITH_CMAKEDIR}/Irccd2
+)
+
 # pkg-config
 if (WITH_PKGCONFIG)
 	# Configure the pkg-config file.
--- a/lib/irccd/CMakeSources.cmake	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/CMakeSources.cmake	Wed Mar 30 13:52:47 2016 +0200
@@ -1,7 +1,3 @@
-include(${CMAKE_CURRENT_LIST_DIR}/command/CMakeSources.cmake)
-include(${CMAKE_CURRENT_LIST_DIR}/js/CMakeSources.cmake)
-include(${CMAKE_CURRENT_LIST_DIR}/private/CMakeSources.cmake)
-
 set(
 	HEADERS
 	${COMMAND_HEADERS}
@@ -9,10 +5,52 @@
 	${PRIVATE_HEADERS}
 	${CMAKE_CURRENT_LIST_DIR}/alias.h
 	${CMAKE_CURRENT_LIST_DIR}/application.h
+	${CMAKE_CURRENT_LIST_DIR}/connection.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-help.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-info.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-list.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-load.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-reload.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-unload.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-cmode.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-cnotice.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-connect.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-disconnect.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-info.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-invite.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-join.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-kick.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-list.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-me.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-message.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-mode.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-nick.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-notice.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-part.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-reconnect.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-topic.h
+	${CMAKE_CURRENT_LIST_DIR}/cmd-watch.h
+	${CMAKE_CURRENT_LIST_DIR}/command.h
 	${CMAKE_CURRENT_LIST_DIR}/config.h
+	${CMAKE_CURRENT_LIST_DIR}/directory.h
+	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h
+	${CMAKE_CURRENT_LIST_DIR}/filesystem.h
+	${CMAKE_CURRENT_LIST_DIR}/ini.h
 	${CMAKE_CURRENT_LIST_DIR}/irccd.h
 	${CMAKE_CURRENT_LIST_DIR}/irccdctl.h
+	${CMAKE_CURRENT_LIST_DIR}/js.h
 	${CMAKE_CURRENT_LIST_DIR}/json.h
+	${CMAKE_CURRENT_LIST_DIR}/js-directory.h
+	${CMAKE_CURRENT_LIST_DIR}/js-elapsed-timer.h
+	${CMAKE_CURRENT_LIST_DIR}/js-file.h
+	${CMAKE_CURRENT_LIST_DIR}/js-irccd.h
+	${CMAKE_CURRENT_LIST_DIR}/js-logger.h
+	${CMAKE_CURRENT_LIST_DIR}/js-plugin.h
+	${CMAKE_CURRENT_LIST_DIR}/js-server.h
+	${CMAKE_CURRENT_LIST_DIR}/js-system.h
+	${CMAKE_CURRENT_LIST_DIR}/js-timer.h
+	${CMAKE_CURRENT_LIST_DIR}/js-unicode.h
+	${CMAKE_CURRENT_LIST_DIR}/js-util.h
 	${CMAKE_CURRENT_LIST_DIR}/logger.h
 	${CMAKE_CURRENT_LIST_DIR}/options.h
 	${CMAKE_CURRENT_LIST_DIR}/path.h
@@ -20,6 +58,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/rule.h
 	${CMAKE_CURRENT_LIST_DIR}/server.h
 	${CMAKE_CURRENT_LIST_DIR}/server-state.h
+	${CMAKE_CURRENT_LIST_DIR}/sockets.h
 	${CMAKE_CURRENT_LIST_DIR}/system.h
 	${CMAKE_CURRENT_LIST_DIR}/timer.h
 	${CMAKE_CURRENT_LIST_DIR}/transport-client.h
@@ -30,15 +69,53 @@
 
 set(
 	SOURCES
-	${COMMAND_SOURCES}
-	${JS_SOURCES}
-	${PRIVATE_SOURCES}
 	${CMAKE_CURRENT_LIST_DIR}/alias.cpp
 	${CMAKE_CURRENT_LIST_DIR}/application.cpp
+	${CMAKE_CURRENT_LIST_DIR}/connection.cpp
 	${CMAKE_CURRENT_LIST_DIR}/config.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-help.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-info.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-list.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-load.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-reload.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-unload.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-cmode.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-cnotice.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-connect.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-disconnect.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-info.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-invite.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-join.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-kick.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-list.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-me.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-message.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-mode.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-nick.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-notice.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-part.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-reconnect.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-server-topic.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd-watch.cpp
+	${CMAKE_CURRENT_LIST_DIR}/command.cpp
+	${CMAKE_CURRENT_LIST_DIR}/directory.cpp
+	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp
+	${CMAKE_CURRENT_LIST_DIR}/filesystem.cpp
+	${CMAKE_CURRENT_LIST_DIR}/ini.cpp
 	${CMAKE_CURRENT_LIST_DIR}/irccd.cpp
 	${CMAKE_CURRENT_LIST_DIR}/irccdctl.cpp
 	${CMAKE_CURRENT_LIST_DIR}/json.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-directory.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-elapsed-timer.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-file.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-irccd.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-logger.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-plugin.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-server.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-system.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-timer.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-unicode.cpp
+	${CMAKE_CURRENT_LIST_DIR}/js-util.cpp
 	${CMAKE_CURRENT_LIST_DIR}/logger.cpp
 	${CMAKE_CURRENT_LIST_DIR}/options.cpp
 	${CMAKE_CURRENT_LIST_DIR}/path.cpp
@@ -46,6 +123,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/rule.cpp
 	${CMAKE_CURRENT_LIST_DIR}/server.cpp
 	${CMAKE_CURRENT_LIST_DIR}/server-state.cpp
+	${CMAKE_CURRENT_LIST_DIR}/sockets.cpp
 	${CMAKE_CURRENT_LIST_DIR}/system.cpp
 	${CMAKE_CURRENT_LIST_DIR}/timer.cpp
 	${CMAKE_CURRENT_LIST_DIR}/transport-client.cpp
@@ -53,3 +131,8 @@
 	${CMAKE_CURRENT_LIST_DIR}/unicode.cpp
 	${CMAKE_CURRENT_LIST_DIR}/util.cpp
 )
+
+if (NOT IRCCD_SYSTEM_WINDOWS)
+	list(APPEND HEADERS ${CMAKE_CURRENT_LIST_DIR}/xdg.h)
+	list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/xdg.cpp)
+endif ()
--- a/lib/irccd/application.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/application.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -17,31 +17,30 @@
  */
 
 #include "application.h"
-
-#include "command/help.h"
-#include "command/plugin-info.h"
-#include "command/plugin-list.h"
-#include "command/plugin-load.h"
-#include "command/plugin-reload.h"
-#include "command/plugin-unload.h"
-#include "command/server-cmode.h"
-#include "command/server-cnotice.h"
-#include "command/server-connect.h"
-#include "command/server-disconnect.h"
-#include "command/server-info.h"
-#include "command/server-invite.h"
-#include "command/server-join.h"
-#include "command/server-kick.h"
-#include "command/server-list.h"
-#include "command/server-me.h"
-#include "command/server-message.h"
-#include "command/server-mode.h"
-#include "command/server-nick.h"
-#include "command/server-notice.h"
-#include "command/server-part.h"
-#include "command/server-reconnect.h"
-#include "command/server-topic.h"
-#include "command/watch.h"
+#include "cmd-help.h"
+#include "cmd-plugin-info.h"
+#include "cmd-plugin-list.h"
+#include "cmd-plugin-load.h"
+#include "cmd-plugin-reload.h"
+#include "cmd-plugin-unload.h"
+#include "cmd-server-cmode.h"
+#include "cmd-server-cnotice.h"
+#include "cmd-server-connect.h"
+#include "cmd-server-disconnect.h"
+#include "cmd-server-info.h"
+#include "cmd-server-invite.h"
+#include "cmd-server-join.h"
+#include "cmd-server-kick.h"
+#include "cmd-server-list.h"
+#include "cmd-server-me.h"
+#include "cmd-server-message.h"
+#include "cmd-server-mode.h"
+#include "cmd-server-nick.h"
+#include "cmd-server-notice.h"
+#include "cmd-server-part.h"
+#include "cmd-server-reconnect.h"
+#include "cmd-server-topic.h"
+#include "cmd-watch.h"
 
 namespace irccd {
 
--- a/lib/irccd/application.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/application.h	Wed Mar 30 13:52:47 2016 +0200
@@ -23,7 +23,7 @@
 #include <memory>
 #include <unordered_map>
 
-#include "command/command.h"
+#include "command.h"
 
 namespace irccd {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-help.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,57 @@
+/*
+ * command-help.cpp -- implementation of irccdctl help
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd/irccdctl.h>
+#include <irccd/logger.h>
+
+#include "cmd-help.h"
+
+namespace irccd {
+
+namespace command {
+
+Help::Help()
+	: RemoteCommand("help", "General")
+{
+}
+
+std::vector<RemoteCommand::Arg> Help::args() const
+{
+	return {{ "command", true }};
+}
+
+std::string Help::help() const
+{
+	return "Get help about a command.";
+}
+
+json::Value Help::request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const
+{
+	auto it = irccdctl.commands().find(args.arg(0U));
+
+	if (it == irccdctl.commands().end())
+		log::warning() << "there is no command named: " << args.arg(0U) << std::endl;
+	else
+		log::warning() << it->second->usage() << std::flush;
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-help.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,58 @@
+/*
+ * command-help.h -- implementation of irccdctl help
+ *
+ * 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 _IRCCDCTL_COMMAND_HELP_H_
+#define _IRCCDCTL_COMMAND_HELP_H_
+
+/**
+ * @file command-help.h
+ * @brief Implementation of irccdctl help.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class Help
+ * @brief Implementation of irccdctl help.
+ */
+class Help : public RemoteCommand {
+public:
+	Help();
+
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-info.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * plugin-info.cpp -- implementation of plugin-info command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iostream>
+
+#include <irccd-config.h>
+
+#include "cmd-plugin-info.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+PluginInfo::PluginInfo()
+	: RemoteCommand("plugin-info", "Plugins")
+{
+}
+
+std::string PluginInfo::help() const
+{
+	return "Get plugin information.";
+}
+
+std::vector<RemoteCommand::Arg> PluginInfo::args() const
+{
+	return {{ "plugin", true }};
+}
+
+json::Value PluginInfo::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({{ "plugin", args.arg(0) }});
+}
+
+json::Value PluginInfo::exec(Irccd &irccd, const json::Value &request) const
+{
+#if defined(WITH_JS)
+	auto plugin = irccd.requirePlugin(request.at("plugin").toString());
+	
+	return json::object({
+		{ "author",	plugin->info().author	},
+		{ "license",	plugin->info().license	},
+		{ "summary",	plugin->info().summary	},
+		{ "version",	plugin->info().version	}
+	});
+#else
+	(void)irccd;
+	(void)object;
+
+	throw std::runtime_error("JavaScript disabled");
+#endif
+}
+
+void PluginInfo::result(Irccdctl &irccdctl, const json::Value &result) const
+{
+	RemoteCommand::result(irccdctl, result);
+
+	/* Plugin information */
+	if (result.valueOr("status", false).toBool()) {
+		std::cout << std::boolalpha;
+		std::cout << "Author         : " << result.valueOr("author", "").toString(true) << std::endl;
+		std::cout << "License        : " << result.valueOr("license", "").toString(true) << std::endl;
+		std::cout << "Summary        : " << result.valueOr("summary", "").toString(true) << std::endl;
+		std::cout << "Version        : " << result.valueOr("version", "").toString(true) << std::endl;
+	}
+}
+
+} // !command
+
+} // !irccd
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-info.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,74 @@
+/*
+ * plugin-info.h -- implementation of plugin-info command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_PLUGIN_INFO_H_
+#define _IRCCD_PLUGIN_INFO_H_
+
+/**
+ * @file plugin-info.h
+ * @brief Implementation of plugin-info transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class PluginInfo
+ * @brief Implementation of plugin-info transport command.
+ */
+class PluginInfo : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	PluginInfo();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+
+	/**
+	 * @copydoc RemoteCommand::result
+	 */
+	void result(Irccdctl &irccdctl, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_PLUGIN_INFO_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-list.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,70 @@
+/*
+ * command-plugin-list.cpp -- implementation of plugin-list transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iostream>
+
+#include <irccd-config.h>
+
+#include "cmd-plugin-list.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+PluginList::PluginList()
+	: RemoteCommand("plugin-list", "Plugins")
+{
+}
+
+std::string PluginList::help() const
+{
+	return "Get the list of loaded plugins.";
+}
+
+json::Value PluginList::exec(Irccd &irccd, const json::Value &request) const
+{
+#if defined(WITH_JS)
+	json::Value response = RemoteCommand::exec(irccd, request);
+	json::Value list = json::array({});
+
+	for (const auto &plugin : irccd.plugins())
+		list.append(plugin.first);
+
+	response.insert("list", std::move(list));
+
+	return response;
+#else
+	(void)irccd;
+	(void)tc;
+
+	throw std::runtime_error("JavaScript disabled");
+#endif
+}
+
+void PluginList::result(Irccdctl &irccdctl, const json::Value &object) const
+{
+	RemoteCommand::result(irccdctl, object);
+
+	for (const auto &n : object.valueOr("list", json::Type::Array, json::array({})))
+		std::cout << n.toString() << std::endl;
+}
+
+} // !command
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-list.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * command-plugin-list.h -- implementation of plugin-list transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_PLUGIN_LIST_H_
+#define _IRCCD_COMMAND_PLUGIN_LIST_H_
+
+/**
+ * @file command-plugin-list.h
+ * @brief Implementation of plugin-list transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class PluginList
+ * @brief Implementation of plugin-list transport command.
+ */
+class PluginList : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	PluginList();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+
+	/**
+	 * @copydoc RemoteCommand::result
+	 */
+	void result(Irccdctl &irccdctl, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_PLUGIN_LIST_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-load.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,62 @@
+/*
+ * command-plugin-load.cpp -- implementation of plugin-load transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd-config.h>
+
+#include "cmd-plugin-load.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+PluginLoad::PluginLoad()
+	: RemoteCommand("plugin-load", "Plugins")
+{
+}
+
+std::string PluginLoad::help() const
+{
+	return "Load a plugin.";
+}
+
+std::vector<RemoteCommand::Arg> PluginLoad::args() const
+{
+	return {{ "plugin", true }};
+}
+
+json::Value PluginLoad::exec(Irccd &irccd, const json::Value &request) const
+{
+#if defined(WITH_JS)
+	auto name = request.at("plugin").toString();
+
+	irccd.loadPlugin(name, name, true);
+
+	return RemoteCommand::exec(irccd, request);
+#else
+	(void)irccd;
+	(void)tc;
+	(void)object;
+
+	throw std::runtime_error("JavaScript disabled");
+#endif
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-load.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * command-plugin-load.h -- implementation of plugin-load transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_PLUGIN_LOAD_H_
+#define _IRCCD_COMMAND_PLUGIN_LOAD_H_
+
+/**
+ * @file plugin-load.h
+ * @brief Implementation of plugin-load transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class PluginLoad
+ * @brief Implementation of plugin-load transport command.
+ */
+class PluginLoad : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	PluginLoad();
+
+	/**
+	 * @copydoc TransportCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_PLUGIN_LOAD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-reload.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,60 @@
+/*
+ * command-plugin-reload.cpp -- implementation of plugin-reload transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd-config.h>
+
+#include "cmd-plugin-reload.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+PluginReload::PluginReload()
+	: RemoteCommand("plugin-reload", "Plugins")
+{
+}
+
+std::string PluginReload::help() const
+{
+	return "Reload a plugin.";
+}
+
+std::vector<RemoteCommand::Arg> PluginReload::args() const
+{
+	return {{ "plugin", true }};
+}
+
+json::Value PluginReload::exec(Irccd &irccd, const json::Value &request) const
+{
+#if defined(WITH_JS)
+	irccd.requirePlugin(request.at("plugin").toString())->onReload();
+
+	return RemoteCommand::exec(irccd, request);
+#else
+	(void)irccd;
+	(void)tc;
+	(void)object;
+
+	throw std::runtime_error("JavaScript disabled");
+#endif
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-reload.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * plugin-reload.h -- implementation of plugin-reload transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_PLUGIN_RELOAD_H_
+#define _IRCCD_COMMAND_PLUGIN_RELOAD_H_
+
+/**
+ * @file command-plugin-load.h
+ * @brief Implementation of plugin-reload transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class PluginReload
+ * @brief Implementation of plugin-reload transport command.
+ */
+class PluginReload : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	PluginReload();
+
+	/**
+	 * @copydoc TransportCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_PLUGIN_RELOAD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-unload.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,60 @@
+/*
+ * plugin-unload.cpp -- implementation of plugin-unload transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd-config.h>
+
+#include "cmd-plugin-unload.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+PluginUnload::PluginUnload()
+	: RemoteCommand("plugin-unload", "Plugins")
+{
+}
+
+std::string PluginUnload::help() const
+{
+	return "Unload a plugin.";
+}
+
+std::vector<RemoteCommand::Arg> PluginUnload::args() const
+{
+	return {{ "plugin", true }};
+}
+
+json::Value PluginUnload::exec(Irccd &irccd, const json::Value &request) const
+{
+#if defined(WITH_JS)
+	irccd.unloadPlugin(request.at("plugin").toString());
+
+	return RemoteCommand::exec(irccd, request);
+#else
+	(void)irccd;
+	(void)tc;
+	(void)object;
+
+	throw std::runtime_error("JavaScript disabled");
+#endif
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-plugin-unload.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * plugin-unload.h -- implementation of plugin-unload transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_PLUGIN_UNLOAD_H_
+#define _IRCCD_COMMAND_PLUGIN_UNLOAD_H_
+
+/**
+ * @file plugin-unload.h
+ * @brief Implementation of plugin-unload transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class PluginUnload
+ * @brief Implementation of plugin-unload transport command.
+ */
+class PluginUnload : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	PluginUnload();
+
+	/**
+	 * @copydoc TransportCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_PLUGIN_UNLOAD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-cmode.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,58 @@
+/*
+ * server-cmode.cpp -- implementation of server-cmode transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-cmode.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerChannelMode::ServerChannelMode()
+	: RemoteCommand("server-cmode", "Server")
+{
+}
+
+std::string ServerChannelMode::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerChannelMode::args() const
+{
+	return {
+		{ "server", true },
+		{ "channel", true },
+		{ "mode", true }
+	};
+}
+
+json::Value ServerChannelMode::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->cmode(
+		request.at("channel").toString(),
+		request.at("mode").toString()
+	);
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-cmode.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * server-cmode.h -- implementation of server-cmode transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
+#define _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
+
+/**
+ * @file server-cmode.h
+ * @brief Implementation of server-cmode transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerChannelMode
+ * @brief Implementation of server-cmode transport command.
+ */
+class ServerChannelMode : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	ServerChannelMode();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+	 
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-cnotice.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,57 @@
+/*
+ * server-cnotice.cpp -- implementation of server-cnotice transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-cnotice.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerChannelNotice::ServerChannelNotice()
+	: RemoteCommand("server-cnotice", "Server")
+{
+}
+
+std::string ServerChannelNotice::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerChannelNotice::args() const
+{
+	return {
+		{ "server", true },
+		{ "channel", true },
+		{ "message", true }
+	};
+}
+
+json::Value ServerChannelNotice::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->cnotice(
+		request.at("channel").toString(),
+		request.at("message").toString()
+	);
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-cnotice.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,73 @@
+/*
+ * server-cnotice.h -- implementation of server-cnotice transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_CNOTICE_H_
+#define _IRCCD_COMMAND_SERVER_CNOTICE_H_
+
+/**
+ * @file server-cnotice.h
+ * @brief Implementation of server-cnotice transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerChannelNotice
+ * @brief Implementation of server-cnotice transport command.
+ *
+ * Send a channel notice to the specified channel.
+ *
+ * {
+ *   "command": "server-cnotice",
+ *   "server": "the server name",
+ *   "channel": "name",
+ *   "message": "the message"
+ * }
+ */
+class ServerChannelNotice : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	ServerChannelNotice();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_CNOTICE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-connect.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,162 @@
+/*
+ * command-server-connect.cpp -- implementation of server-connect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <limits>
+
+#include "cmd-server-connect.h"
+#include "irccd.h"
+#include "server.h"
+#include "util.h"
+
+namespace irccd {
+
+namespace command {
+
+namespace {
+
+std::string readInfoName(const json::Value &object)
+{
+	auto it = object.find("name");
+
+	if (it == object.end())
+		throw std::invalid_argument("missing 'name' property");
+	if (!it->isString() || !util::isIdentifierValid(it->toString()))
+		throw std::invalid_argument("invalid server name");
+
+	return it->toString();
+}
+
+std::string readInfoHost(const json::Value &object)
+{
+	auto it = object.find("host");
+
+	if (it == object.end())
+		throw std::invalid_argument("missing 'host' property");
+	if (!it->isString())
+		throw std::invalid_argument("invalid host");
+
+	return it->toString();
+}
+
+std::uint16_t readInfoPort(const json::Value &object)
+{
+	auto it = object.find("port");
+	uint16_t port = 6667;
+
+	if (it != object.end())
+		if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max())
+			port = static_cast<std::uint16_t>(it->toInt());
+
+	return port;
+}
+
+ServerInfo readInfo(const json::Value &object)
+{
+	ServerInfo info;
+
+	/* Mandatory */
+	info.name = readInfoName(object);
+	info.host = readInfoHost(object);
+
+	/* Optional */
+	info.port = readInfoPort(object);
+
+	if (object.valueOr("ssl", json::Type::Boolean, false).toBool())
+#if defined(WITH_SSL)
+		info.flags |= ServerInfo::Ssl;
+#else
+		throw std::invalid_argument("ssl is disabled");
+#endif
+
+	if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool())
+		info.flags |= ServerInfo::SslVerify;
+
+	return info;
+}
+
+ServerIdentity readIdentity(const json::Value &object)
+{
+	ServerIdentity identity;
+
+	identity.nickname = object.valueOr("nickname", json::Type::String, identity.nickname).toString();
+	identity.realname = object.valueOr("realname", json::Type::String, identity.realname).toString();
+	identity.username = object.valueOr("username", json::Type::String, identity.username).toString();
+	identity.ctcpversion = object.valueOr("ctcpVersion", json::Type::String, identity.ctcpversion).toString();
+
+	return identity;
+}
+
+ServerSettings readSettings(const json::Value &object)
+{
+	ServerSettings settings;
+
+	settings.command = object.valueOr("commandChar", json::Type::String, settings.command).toString();
+	settings.recotries = object.valueOr("reconnectTries", json::Type::Int, settings.recotries).toInt();
+	settings.recotimeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.recotimeout).toInt();
+
+	return settings;
+}
+
+} // !namespace
+
+ServerConnect::ServerConnect()
+	: RemoteCommand("server-connect", "Server")
+{
+}
+
+std::string ServerConnect::help() const
+{
+	return "Connect to a server.";
+}
+
+std::vector<RemoteCommand::Option> ServerConnect::options() const
+{
+	return {
+		{ "command", "c", "command", "char", "command character to use" },
+		{ "nickname", "n", "nickname", "nickname", "nickname to use" },
+		{ "realname", "r", "realname", "realname", "realname to use" },
+		{ "sslverify", "S", "ssl-verify", "", "verify SSL" },
+		{ "ssl", "s", "ssl", "", "connect with SSL" },
+		{ "username", "u", "username", "", "username to use" },
+	};
+}
+
+std::vector<RemoteCommand::Arg> ServerConnect::args() const
+{
+	return {
+		{ "id", true },
+		{ "host", true },
+		{ "port", false }
+	};
+}
+
+json::Value ServerConnect::exec(Irccd &irccd, const json::Value &request) const
+{
+	auto server = std::make_shared<Server>(readInfo(request), readIdentity(request), readSettings(request));
+
+	if (irccd.hasServer(server->info().name))
+		throw std::invalid_argument("server '" + server->info().name + "' already exists");
+
+	irccd.addServer(std::move(server));
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-connect.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,69 @@
+/*
+ * server-connect.h -- implementation of server-connect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_CONNECT_H_
+#define _IRCCD_COMMAND_SERVER_CONNECT_H_
+
+/**
+ * @file server-connect.h
+ * @brief Implementation of server-connect transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerConnect
+ * @brief Implementation of server-connect transport command.
+ */
+class ServerConnect : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	ServerConnect();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::options
+	 */
+	std::vector<Option> options() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_CONNECT_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-disconnect.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,55 @@
+/*
+ * command-server-disconnect.cpp -- implementation of server-disconnect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-disconnect.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerDisconnect::ServerDisconnect()
+	: RemoteCommand("server-disconnect", "Server")
+{
+}
+
+std::string ServerDisconnect::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerDisconnect::args() const
+{
+	return {{ "server", false }};
+}
+
+json::Value ServerDisconnect::exec(Irccd &irccd, const json::Value &request) const
+{
+	auto it = request.find("server");
+
+	if (it == request.end())
+		irccd.clearServers();
+	else
+		irccd.removeServer(it->toString());
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-disconnect.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,61 @@
+/*
+ * server-disconnect.h -- implementation of server-disconnect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_DISCONNECT_H_
+#define _IRCCD_COMMAND_SERVER_DISCONNECT_H_
+
+/**
+ * @file server-disconnect.h
+ * @brief Implementation of server-disconnect transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerDisconnect
+ * @brief Implementation of server-disconnect transport command.
+ */
+class ServerDisconnect : public RemoteCommand {
+public:
+	/**
+	 * Constructor.
+	 */
+	ServerDisconnect();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_DISCONNECT_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-info.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,113 @@
+/*
+ * command-server-info.cpp -- implementation of server-info transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iostream>
+
+#include "cmd-server-info.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerInfo::ServerInfo()
+	: RemoteCommand("server-info", "Server")
+{
+}
+
+std::string ServerInfo::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerInfo::args() const
+{
+	return {{ "server", true }};
+}
+
+json::Value ServerInfo::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server",	args.args()[0]		},
+		{ "target",	args.args()[1]		},
+		{ "channel",	args.args()[2]		}
+	});
+}
+
+json::Value ServerInfo::exec(Irccd &irccd, const json::Value &request) const
+{
+	auto server = irccd.requireServer(request.at("server").toString());
+	auto response = RemoteCommand::exec(irccd, request);
+
+	/* General stuff */
+	response.insert("name", server->info().name);
+	response.insert("host", server->info().host);
+	response.insert("port", server->info().port);
+	response.insert("nickname", server->identity().nickname);
+	response.insert("username", server->identity().username);
+	response.insert("realname", server->identity().realname);
+
+	/* Optional stuff */
+	if (server->info().flags & irccd::ServerInfo::Ipv6)
+		response.insert("ipv6", true);
+	if (server->info().flags & irccd::ServerInfo::Ssl)
+		response.insert("ssl", true);
+	if (server->info().flags & irccd::ServerInfo::SslVerify)
+		response.insert("sslVerify", true);
+
+	/* Channel list */
+	auto channels = json::array({});
+
+	for (const auto &c : server->settings().channels)
+		channels.append(c.name);
+
+	response.insert("channels", std::move(channels));
+
+	return response;
+}
+
+void ServerInfo::result(Irccdctl &irccdctl, const json::Value &response) const
+{
+	RemoteCommand::result(irccdctl, response);
+
+	/* Server information */
+	std::cout << std::boolalpha;
+	std::cout << "Name           : " << response.valueOr("name", "").toString(true) << std::endl;
+	std::cout << "Host           : " << response.valueOr("host", "").toString(true) << std::endl;
+	std::cout << "Port           : " << response.valueOr("port", "").toString(true) << std::endl;
+	std::cout << "Ipv6           : " << response.valueOr("ipv6", "").toString(true) << std::endl;
+	std::cout << "SSL            : " << response.valueOr("ssl", "").toString(true) << std::endl;
+	std::cout << "SSL verified   : " << response.valueOr("sslVerify", "").toString(true) << std::endl;
+
+	/* Channels */
+	std::cout << "Channels       : ";
+
+	for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({})))
+		std::cout << v.toString() << " ";
+
+	std::cout << std::endl;
+
+	/* Identity */
+	std::cout << "Nickname       : " << response.valueOr("nickname", "").toString(true) << std::endl;
+	std::cout << "User name      : " << response.valueOr("username", "").toString(true) << std::endl;
+	std::cout << "Real name      : " << response.valueOr("realname", "").toString(true) << std::endl;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-info.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,65 @@
+/*
+ * command-server-info.h -- implementation of server-info transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_INFO_H_
+#define _IRCCD_COMMAND_SERVER_INFO_H_
+
+/**
+ * @file command-server-info.h
+ * @brief Implementation of server-info transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerInfo
+ * @brief Implementation of server-info transport command.
+ */
+class ServerInfo : public RemoteCommand {
+public:
+	ServerInfo();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &object) const override;
+
+	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_INFO_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-invite.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,68 @@
+/*
+ * command-server-invite.cpp -- implementation of server-invite transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-invite.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerInvite::ServerInvite()
+	: RemoteCommand("server-invite", "Server")
+{
+}
+
+std::string ServerInvite::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerInvite::args() const
+{
+	return {
+		{ "server", true },
+		{ "nickname", true },
+		{ "channel", true }
+	};
+}
+
+json::Value ServerInvite::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server",	args.args()[0]		},
+		{ "target",	args.args()[1]		},
+		{ "channel",	args.args()[2]		}
+	});
+}
+
+json::Value ServerInvite::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(
+		request.at("server").toString())->invite(
+		request.at("target").toString(),
+		request.at("channel").toString()
+	);
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-invite.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,68 @@
+/*
+ * server-invite.h -- implementation of server-invite transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_INVITE_H_
+#define _IRCCD_COMMAND_SERVER_INVITE_H_
+
+/**
+ * @file server-invite.h
+ * @brief Implementation of server-invite transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerInvite
+ * @brief Implementation of server-invite transport command.
+ */
+class ServerInvite : public RemoteCommand {
+public:
+	ServerInvite();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc TransportCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+
+	
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_INVITE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-join.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,71 @@
+/*
+ * server-join.cpp -- implementation of server-join transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-join.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerJoin::ServerJoin()
+	: RemoteCommand("server-join", "Server")
+{
+}
+
+std::string ServerJoin::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerJoin::args() const
+{
+	return {
+		{ "server", true },
+		{ "channel", true },
+		{ "password", false }
+	};
+}
+
+json::Value ServerJoin::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	auto req = json::object({
+		{ "server", args.args()[0] },
+		{ "channel", args.args()[1] }
+	});
+
+	if (args.length() == 3)
+		req.insert("password", args.args()[2]);
+
+	return req;
+}
+
+json::Value ServerJoin::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(
+		request.at("server").toString())->join(
+		request.at("channel").toString(),
+		request.valueOr("password", json::Type::String, "").toString()
+	);
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-join.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-join.h -- implementation of server-join transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_JOIN_H_
+#define _IRCCD_COMMAND_SERVER_JOIN_H_
+
+/**
+ * @file server-join.h
+ * @brief Implementation of server-join transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerJoin
+ * @brief Implementation of server-join transport command.
+ */
+class ServerJoin : public RemoteCommand {
+public:
+	ServerJoin();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_JOIN_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-kick.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,73 @@
+/*
+ * command-server-kick.cpp -- implementation of server-kick transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-kick.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerKick::ServerKick()
+	: RemoteCommand("server-kick", "Server")
+{
+}
+
+std::string ServerKick::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerKick::args() const
+{
+	return {
+		{ "server", true },
+		{ "target", true },
+		{ "channel", true },
+		{ "reason", false }
+	};
+}
+
+json::Value ServerKick::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	auto req = json::object({
+		{ "server", args.arg(0) },
+		{ "target", args.arg(1) },
+		{ "channel", args.arg(2) }
+	});
+
+	if (args.length() == 4)
+		req.insert("reason", args.arg(3));
+
+	return req;
+}
+
+json::Value ServerKick::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->kick(
+		request.at("target").toString(),
+		request.at("channel").toString(),
+		request.valueOr("reason", json::Type::String, "").toString()
+	);
+
+	return RemoteCommand::exec(irccd, request);
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-kick.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-kick.h -- implementation of server-kick transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_KICK_H_
+#define _IRCCD_COMMAND_SERVER_KICK_H_
+
+/**
+ * @file server-kick.h
+ * @brief Implementation of server-kick transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerKick
+ * @brief Implementation of server-kick transport command.
+ */
+class ServerKick : public RemoteCommand {
+public:
+	ServerKick();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_KICK_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-list.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,59 @@
+/*
+ * server-list.cpp -- implementation of server-list transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iostream>
+
+#include "cmd-server-list.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerList::ServerList()
+	: RemoteCommand("server-list", "Server")
+{
+}
+
+std::string ServerList::help() const
+{
+	return "";
+}
+
+json::Value ServerList::exec(Irccd &irccd, const json::Value &) const
+{
+	auto json = json::object({});
+	auto list = json::array({});
+
+	for (const auto &pair : irccd.servers())
+		list.append(pair.first);
+
+	json.insert("list", std::move(list));
+
+	return json;
+}
+
+void ServerList::result(Irccdctl &, const json::Value &response) const
+{
+	for (const auto &n : response.valueOr("list", json::Type::Array, json::array({})))
+		std::cout << n.toString() << std::endl;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-list.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,55 @@
+/*
+ * server-list.h -- implementation of server-list transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_LIST_H_
+#define _IRCCD_COMMAND_SERVER_LIST_H_
+
+/**
+ * @file command-server-list.h
+ * @brief Implementation of server-list transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerList
+ * @brief Implementation of server-list transport command.
+ */
+class ServerList : public RemoteCommand {
+public:
+	ServerList();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+
+	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_LIST_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-me.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-me.cpp -- implementation of server-me transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-me.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerMe::ServerMe()
+	: RemoteCommand("server-me", "Server")
+{
+}
+
+std::string ServerMe::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerMe::args() const
+{
+	return {
+		{ "server", true },
+		{ "target", true },
+		{ "message", true }
+	};
+}
+
+json::Value ServerMe::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "target", args.arg(1) },
+		{ "message", args.arg(2) }
+	});
+}
+
+json::Value ServerMe::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->me(
+		request.at("target").toString(),
+		request.at("message").toString()
+	);
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-me.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-me.h -- implementation of server-me transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_ME_H_
+#define _IRCCD_COMMAND_SERVER_ME_H_
+
+/**
+ * @file command-.h
+ * @brief Implementation of  transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerMe
+ * @brief Implementation of server-me transport command.
+ */
+class ServerMe : public RemoteCommand {
+public:
+	ServerMe();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_ME_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-message.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-message.cpp -- implementation of server-message transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-message.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerMessage::ServerMessage()
+	: RemoteCommand("server-message", "Server")
+{
+}
+
+std::string ServerMessage::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerMessage::args() const
+{
+	return {
+		{ "server", true },
+		{ "target", true },
+		{ "message", true }
+	};
+}
+
+json::Value ServerMessage::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "target", args.arg(1) },
+		{ "message", args.arg(2) }
+	});
+}
+
+json::Value ServerMessage::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->me(
+		request.at("target").toString(),
+		request.at("message").toString()
+	);
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-message.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-message.h -- implementation of server-message transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_MESSAGE_H_
+#define _IRCCD_COMMAND_SERVER_MESSAGE_H_
+
+/**
+ * @file command-server-message.h
+ * @brief Implementation of server-message transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerMessage
+ * @brief Implementation of server-message transport command.
+ */
+class ServerMessage : public RemoteCommand {
+public:
+	ServerMessage();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_MESSAGE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-mode.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,61 @@
+/*
+ * command-server-mode.cpp -- implementation of server-mode transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-mode.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerMode::ServerMode()
+	: RemoteCommand("server-mode", "Server")
+{
+}
+
+std::string ServerMode::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerMode::args() const
+{
+	return {
+		{ "server", true },
+		{ "mode", true }
+	};
+}
+
+json::Value ServerMode::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "mode", args.arg(1) }
+	});
+}
+
+json::Value ServerMode::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->mode(request.at("mode").toString());
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-mode.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-mode.h -- implementation of server-mode transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_MODE_H_
+#define _IRCCD_COMMAND_SERVER_MODE_H_
+
+/**
+ * @file server-mode.h
+ * @brief Implementation of server-mode transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerMode
+ * @brief Implementation of server-mode transport command.
+ */
+class ServerMode : public RemoteCommand {
+public:
+	ServerMode();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_MODE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-nick.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,61 @@
+/*
+ * server-nick.cpp -- implementation of server-nick transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-nick.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerNick::ServerNick()
+	: RemoteCommand("server-nick", "Server")
+{
+}
+
+std::string ServerNick::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerNick::args() const
+{
+	return {
+		{ "server", true },
+		{ "nickname", true }
+	};
+}
+
+json::Value ServerNick::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "nickname", args.arg(1) }
+	});
+}
+
+json::Value ServerNick::exec(Irccd &irccd, const json::Value &object) const
+{
+	irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString());
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-nick.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-nick.h -- implementation of server-nick transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_NICK_H_
+#define _IRCCD_COMMAND_SERVER_NICK_H_
+
+/**
+ * @file command-server-nick.h
+ * @brief Implementation of server-nick transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerNick
+ * @brief Implementation of server-nick transport command.
+ */
+class ServerNick : public RemoteCommand {
+public:
+	ServerNick();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_NICK_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-notice.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,67 @@
+/*
+ * server-notice.cpp -- implementation of server-notice transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-notice.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerNotice::ServerNotice()
+	: RemoteCommand("server-notice", "Server")
+{
+}
+
+std::string ServerNotice::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerNotice::args() const
+{
+	return {
+		{ "server", true },
+		{ "target", true },
+		{ "message", true }
+	};
+}
+
+json::Value ServerNotice::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "target", args.arg(1) },
+		{ "message", args.arg(2) }
+	});
+}
+
+json::Value ServerNotice::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->notice(
+		request.at("target").toString(),
+		request.at("message").toString()
+	);
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-notice.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-notice.h -- implementation of server-notice transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_NOTICE_H_
+#define _IRCCD_COMMAND_SERVER_NOTICE_H_
+
+/**
+ * @file server-notice.h
+ * @brief Implementation of server-notice transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerNotice
+ * @brief Implementation of server-notice transport command.
+ */
+class ServerNotice : public RemoteCommand {
+public:
+	ServerNotice();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_NOTICE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-part.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,70 @@
+/*
+ * server-part.cpp -- implementation of server-part transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-part.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerPart::ServerPart()
+	: RemoteCommand("server-part", "Server")
+{
+}
+
+std::string ServerPart::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerPart::args() const
+{
+	return {
+		{ "server", true },
+		{ "channel", true },
+		{ "reason", false }
+	};
+}
+
+json::Value ServerPart::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	auto req = json::object({
+		{ "server", args.arg(0) },
+		{ "channel", args.arg(1) }
+	});
+
+	if (args.length() == 3)
+		req.insert("reason", args.arg(2));
+
+	return req;
+}
+
+json::Value ServerPart::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->part(
+		request.at("channel").toString(),
+		request.valueOr("reason", "").toString()
+	);
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-part.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-part.h -- implementation of server-part transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_PART_H_
+#define _IRCCD_COMMAND_SERVER_PART_H_
+
+/**
+ * @file server-part.h
+ * @brief Implementation of server-part transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerPart
+ * @brief Implementation of server-part transport command.
+ */
+class ServerPart : public RemoteCommand {
+public:
+	ServerPart();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_PART_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-reconnect.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,61 @@
+/*
+ * command-server-reconnect.cpp -- implementation of server-reconnect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-reconnect.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerReconnect::ServerReconnect()
+	: RemoteCommand("server-reconnect", "Server")
+{
+}
+
+std::string ServerReconnect::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerReconnect::args() const
+{
+	return {{ "server", false }};
+}
+
+json::Value ServerReconnect::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return args.length() == 0 ? nullptr : json::object({ { "server", args.arg(0) } });
+}
+
+json::Value ServerReconnect::exec(Irccd &irccd, const json::Value &request) const
+{
+	auto server = request.find("server");
+
+	if (server != request.end() && server->isString())
+		irccd.requireServer(server->toString())->reconnect();
+	else
+		for (auto &pair : irccd.servers())
+			pair.second->reconnect();
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-reconnect.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-reconnect.h -- implementation of server-reconnect transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_RECONNECT_H_
+#define _IRCCD_COMMAND_SERVER_RECONNECT_H_
+
+/**
+ * @file server-reconnect.h
+ * @brief Implementation of server-reconnect transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerReconnect
+ * @brief Implementation of server-reconnect transport command.
+ */
+class ServerReconnect : public RemoteCommand {
+public:
+	ServerReconnect();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_RECONNECT_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-topic.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * command-server-topic.cpp -- implementation of server-topic transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cmd-server-topic.h"
+#include "irccd.h"
+
+namespace irccd {
+
+namespace command {
+
+ServerTopic::ServerTopic()
+	: RemoteCommand("server-topic", "Server")
+{
+}
+
+std::string ServerTopic::help() const
+{
+	return "";
+}
+
+std::vector<RemoteCommand::Arg> ServerTopic::args() const
+{
+	return {
+		{ "server", true },
+		{ "channel", true },
+		{ "topic", true }
+	};
+}
+
+json::Value ServerTopic::request(Irccdctl &, const RemoteCommandRequest &args) const
+{
+	return json::object({
+		{ "server", args.arg(0) },
+		{ "channel", args.arg(1) },
+		{ "topic", args.arg(2) }
+	});
+}
+
+json::Value ServerTopic::exec(Irccd &irccd, const json::Value &request) const
+{
+	irccd.requireServer(request.at("server").toString())->topic(
+		request.at("channel").toString(),
+		request.at("topic").toString()
+	);
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-server-topic.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * server-topic.h -- implementation of server-topic transport command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IRCCD_COMMAND_SERVER_TOPIC_H_
+#define _IRCCD_COMMAND_SERVER_TOPIC_H_
+
+/**
+ * @file server-topic.h
+ * @brief Implementation of server-topic transport command.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class ServerTopic
+ * @brief Implementation of server-topic transport command.
+ */
+class ServerTopic : public RemoteCommand {
+public:
+	ServerTopic();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::args
+	 */
+	std::vector<Arg> args() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+
+	/**
+	 * @copydoc RemoteCommand::exec
+	 */
+	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCD_COMMAND_SERVER_TOPIC_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-watch.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,244 @@
+/*
+ * command-watch.cpp -- implementation of irccdctl watch
+ *
+ * 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 <functional>
+#include <iostream>
+#include <sstream>
+#include <unordered_map>
+
+#include "cmd-watch.h"
+#include "irccdctl.h"
+
+namespace irccd {
+
+namespace command {
+
+namespace {
+
+void onChannelMode(const json::Value &v)
+{
+	std::cout << "event:       onChannelMode\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "mode:        " << v.valueOr("mode", "").toString() << "\n";
+	std::cout << "argument:    " << v.valueOr("argument", "").toString() << "\n";
+}
+
+void onChannelNotice(const json::Value &v)
+{
+	std::cout << "event:       onChannelNotice\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
+}
+
+void onConnect(const json::Value &v)
+{
+	std::cout << "event:       onConnect\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+}
+
+void onInvite(const json::Value &v)
+{
+	std::cout << "event:       onInvite\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+}
+
+void onJoin(const json::Value &v)
+{
+	std::cout << "event:       onJoin\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+}
+
+void onKick(const json::Value &v)
+{
+	std::cout << "event:       onKick\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "target:      " << v.valueOr("target", "").toString() << "\n";
+	std::cout << "reason:      " << v.valueOr("reason", "").toString() << "\n";
+}
+
+void onMessage(const json::Value &v)
+{
+	std::cout << "event:       onMessage\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
+}
+
+void onMe(const json::Value &v)
+{
+	std::cout << "event:       onMe\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "target:      " << v.valueOr("target", "").toString() << "\n";
+	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
+}
+
+void onMode(const json::Value &v)
+{
+	std::cout << "event:       onMode\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "mode:        " << v.valueOr("mode", "").toString() << "\n";
+}
+
+void onNames(const json::Value &v)
+{
+	std::cout << "event:       onNames\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "names:       " << v.valueOr("names", "").toJson(0) << "\n";
+}
+
+void onNick(const json::Value &v)
+{
+	std::cout << "event:       onNick\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "nickname:    " << v.valueOr("nickname", "").toString() << "\n";
+}
+
+void onNotice(const json::Value &v)
+{
+	std::cout << "event:       onNotice\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "message:      " << v.valueOr("message", "").toString() << "\n";
+}
+
+void onPart(const json::Value &v)
+{
+	std::cout << "event:       onPart\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "reason:      " << v.valueOr("reason", "").toString() << "\n";
+}
+
+void onQuery(const json::Value &v)
+{
+	std::cout << "event:       onQuery\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
+}
+
+void onTopic(const json::Value &v)
+{
+	std::cout << "event:       onTopic\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
+	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
+	std::cout << "topic:       " << v.valueOr("topic", "").toString() << "\n";
+}
+
+void onWhois(const json::Value &v)
+{
+	std::cout << "event:       onWhois\n";
+	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
+	std::cout << "nickname:    " << v.valueOr("nickname", "").toString() << "\n";
+	std::cout << "username:    " << v.valueOr("username", "").toString() << "\n";
+	std::cout << "host:        " << v.valueOr("host", "").toString() << "\n";
+	std::cout << "realname:    " << v.valueOr("realname", "").toString() << "\n";
+}
+
+const std::unordered_map<std::string, std::function<void (const json::Value &)>> events{
+	{ "onChannelMode",	onChannelMode		},
+	{ "onChannelNotice",	onChannelNotice		},
+	{ "onConnect",		onConnect		},
+	{ "onInvite",		onInvite		},
+	{ "onJoin",		onJoin			},
+	{ "onKick",		onKick			},
+	{ "onMessage",		onMessage		},
+	{ "onMe",		onMe			},
+	{ "onMode",		onMode			},
+	{ "onNames",		onNames			},
+	{ "onNick",		onNick			},
+	{ "onNotice",		onNotice		},
+	{ "onPart",		onPart			},
+	{ "onQuery",		onQuery			},
+	{ "onTopic",		onTopic			},
+	{ "onWhois",		onWhois			}
+};
+
+} // !namespace
+
+Watch::Watch()
+	: RemoteCommand("watch", "General")
+{
+}
+
+std::vector<RemoteCommand::Option> Watch::options() const
+{
+	return {{ "format", "f", "format", "format", "output format" }};
+}
+
+std::string Watch::help() const
+{
+	std::ostringstream oss;
+
+	oss << "Start watching irccd events.\n\n";
+	oss << "You can use different output formats, native which is a human readable\n";
+	oss << "format or json, pretty formatted json.";
+
+	return oss.str();
+}
+
+json::Value Watch::request(Irccdctl &ctl, const RemoteCommandRequest &request) const
+{
+	std::string format = request.optionOr("format", "native");
+
+	if (format != "native" && format != "json")
+		throw std::invalid_argument("invalid format given: " + format);
+
+	while (ctl.connection().isConnected()) {
+		try {
+			auto object = ctl.connection().next(-1);
+			auto it = events.find(object.valueOr("event", "").toString());
+
+			/* Silently ignore to avoid breaking user output */
+			if (it == events.end())
+				continue;
+
+			if (format == "json") {
+				std::cout << object.toJson() << std::endl;
+			} else {
+				it->second(object);
+				std::cout << std::endl;
+			}
+		} catch (...) {
+		}
+	}
+
+	throw std::runtime_error("connection lost");
+
+	return nullptr;
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/cmd-watch.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,58 @@
+/*
+ * command-watch.h -- implementation of irccdctl watch
+ *
+ * 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 _IRCCDCTL_COMMAND_WATCH_H_
+#define _IRCCDCTL_COMMAND_WATCH_H_
+
+/**
+ * @file command-watch.h
+ * @brief Implementation of irccdctl watch.
+ */
+
+#include "command.h"
+
+namespace irccd {
+
+namespace command {
+
+/**
+ * @class Watch
+ * @brief Implementation of irccdctl watch.
+ */
+class Watch : public RemoteCommand {
+public:
+	Watch();
+
+	std::vector<Option> options() const override;
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+
+	/**
+	 * @copydoc RemoteCommand::request
+	 */
+	json::Value request(Irccdctl &ctl, const RemoteCommandRequest &request) const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,107 @@
+/*
+ * command.cpp -- remote command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+
+#include <irccd/logger.h>
+#include <irccd/system.h>
+
+#include "command.h"
+
+using namespace std::string_literals;
+
+namespace irccd {
+
+std::string RemoteCommand::usage() const
+{
+	std::ostringstream oss;
+
+	oss << "usage: " << sys::programName() << " " << m_name;
+
+	/* Options summary */
+	if (options().size() > 0)
+		oss << " [options...]";
+
+	/* Arguments summary */
+	if (args().size() > 0) {
+		oss << " ";
+
+		for (const auto &arg : args())
+			oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " ";
+	}
+
+	/* Description */
+	oss << "\n\n" << help() << "\n\n";
+
+	/* Options */
+	if (options().size() > 0) {
+		oss << "Options:\n";
+
+		for (const auto &opt : options()) {
+			std::ostringstream optoss;
+
+			/* Construct the line for the option in a single string to pad it correctly */
+			optoss << "  ";
+			optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : "   ");
+			optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : "");
+			optoss << opt.arg();
+
+			/* Add it padded with spaces */
+			oss << std::left << std::setw(28) << optoss.str();
+			oss << opt.description() << "\n";
+		}
+	}
+
+	return oss.str();
+}
+
+unsigned RemoteCommand::min() const noexcept
+{
+	auto list = args();
+
+	return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned {
+		return i + (arg.required() ? 1 : 0);
+	});
+}
+
+unsigned RemoteCommand::max() const noexcept
+{
+	return (unsigned)args().size();
+}
+
+json::Value RemoteCommand::request(Irccdctl &, const RemoteCommandRequest &) const
+{
+	return json::object({});
+}
+
+json::Value RemoteCommand::exec(Irccd &, const json::Value &) const
+{
+	return json::object({});
+}
+
+void RemoteCommand::result(Irccdctl &, const json::Value &response) const
+{
+	auto it = response.find("error");
+
+	if (it != response.end() && it->isString())
+		log::warning() << "irccdctl: " << it->toString() << std::endl;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,469 @@
+/*
+ * command.h -- remote command
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_COMMAND_H
+#define IRCCD_COMMAND_H
+
+#include <cassert>
+#include <map>
+#include <vector>
+
+#include <irccd/json.h>
+
+namespace irccd {
+
+class Irccd;
+class Irccdctl;
+
+/**
+ * @brief Command line arguments to irccdctl.
+ *
+ * This class contains the resolved arguments from command line that can apply to the command.
+ */
+class RemoteCommandRequest {
+public:
+	using Options = std::multimap<std::string, std::string>;
+	using Args = std::vector<std::string>;
+
+private:
+	Options m_options;
+	Args m_args;
+
+public:
+	/**
+	 * Construct the request.
+	 *
+	 * @param options the options
+	 * @param args the arguments
+	 */
+	inline RemoteCommandRequest(Options options, Args args) noexcept
+		: m_options(std::move(options))
+		, m_args(std::move(args))
+	{
+	}
+
+	/**
+	 * Get the arguments.
+	 *
+	 * @return the arguments
+	 */
+	inline const Args &args() const noexcept
+	{
+		return m_args;
+	}
+
+	/**
+	 * Get the options.
+	 *
+	 * @return the options
+	 */
+	inline const Options &options() const noexcept
+	{
+		return m_options;
+	}
+
+	/**
+	 * Get the number of arguments.
+	 *
+	 * @return the number of arguments
+	 */
+	inline unsigned length() const noexcept
+	{
+		return (unsigned)m_args.size();
+	}
+
+	/**
+	 * Check if the request has the given option id.
+	 *
+	 * @param option the option id
+	 * @return true if the option is available
+	 */
+	inline bool has(const std::string &option) const noexcept
+	{
+		return m_options.count(option) != 0;
+	}
+
+	/**
+	 * Get the argument at the specified index.
+	 *
+	 * @pre index < length()
+	 * @param index the argument index
+	 * @return the argument
+	 */
+	inline const std::string &arg(unsigned index) const noexcept
+	{
+		assert(index < m_args.size());
+
+		return m_args[index];
+	}
+
+	/**
+	 * Get the argument or default value if not available.
+	 *
+	 * @param index the index
+	 * @param defaultValue the value if index is out of range
+	 * @return the argument
+	 */
+	inline std::string argOr(unsigned index, std::string defaultValue) const noexcept
+	{
+		return index < m_args.size() ? m_args[index] : defaultValue;
+	}
+
+	/**
+	 * Get the given option by its id.
+	 *
+	 * @pre has(key)
+	 * @param key the option id
+	 * @return the option
+	 */
+	inline const std::string &option(const std::string &key) const noexcept
+	{
+		assert(m_options.count(key) != 0);
+
+		return m_options.find(key)->second;
+	}
+
+	/**
+	 * Get the given option by its id or defaultValue if not found.
+	 *
+	 * @param key the option id
+	 * @return the option
+	 */
+	inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept
+	{
+		auto it = m_options.find(key);
+
+		if (it == m_options.end())
+			return defaultValue;
+
+		return it->second;
+	}
+};
+
+/**
+ * @brief Invokable command.
+ *
+ * A remote command is a invokable command in the irccd daemon. You can register dynamically any remote command you
+ * like using Application::addCommand.
+ *
+ * The remote command will be usable directly from irccdctl without any other code.
+ *
+ * A remote command can have options and arguments. Options always come first, before arguments.
+ *
+ * The command workflow is defined as follow:
+ *
+ * 1. User wants to invoke a command, request() is called and return a JSON object containaing the request, it it send
+ *    to the daemon.
+ *
+ * 2. The daemon receive the request and execute it using exec(). It returns a JSON object containint the request result
+ *    or error if any.
+ *
+ * 3. Finally, the command receives the result in result() function and user can manipulate it. For convenience, the
+ *    default implementation shows the error if any.
+ */
+class RemoteCommand {
+public:
+	/**
+	 * @brief Defines available options for this command.
+	 */
+	class Option;
+
+	/**
+	 * @brief Defines available arguments for this command.
+	 */
+	class Arg;
+
+private:
+	std::string m_name;
+	std::string m_category;
+	bool m_visible;
+
+public:
+	inline RemoteCommand(std::string name, std::string category, bool visible = true) noexcept
+		: m_name(std::move(name))
+		, m_category(std::move(category))
+		, m_visible(visible)
+	{
+		assert(!m_name.empty());
+		assert(!m_category.empty());
+	}
+
+	/**
+	 * Default destructor virtual.
+	 */
+	virtual ~RemoteCommand() = default;
+
+	/**
+	 * Return the command name, must not have spaces.
+	 *
+	 * @return the command name
+	 */
+	inline const std::string &name() const noexcept
+	{
+		return m_name;
+	}
+
+	/**
+	 * Get the command category.
+	 *
+	 * Irccdctl will sort commands by categories.
+	 *
+	 * @return the category
+	 */
+	inline const std::string &category() const noexcept
+	{
+		return m_category;
+	}
+
+	/**
+	 * Hide the command in non-verbose mode.
+	 *
+	 * @return true if the command should be visible in non-verbose mode
+	 */
+	inline bool visible() const noexcept
+	{
+		return m_visible;
+	}
+
+	/**
+	 * Return the command documentation usage.
+	 *
+	 * @return the usage
+	 */
+	std::string usage() const;
+
+	/**
+	 * Return the help message.
+	 *
+	 * @return the help message
+	 */
+	virtual std::string help() const = 0;
+
+	/**
+	 * Get the supported irccdctl options.
+	 *
+	 * @return the options
+	 */
+	virtual std::vector<Option> options() const
+	{
+		return {};
+	}
+
+	/**
+	 * Get the supported arguments.
+	 *
+	 * @return the arguments
+	 */
+	virtual std::vector<Arg> args() const
+	{
+		return {};
+	}
+
+	/**
+	 * Get the minimum number of arguments required.
+	 *
+	 * @return the minimum
+	 */
+	unsigned min() const noexcept;
+
+	/**
+	 * Get the maximum number of arguments required.
+	 *
+	 * @return the maximum
+	 */
+	unsigned max() const noexcept;
+
+	/**
+	 * Prepare a JSON request to the daemon.
+	 *
+	 * If the command is local and does not need to send anything to irccd's instance, return a null JSON value.
+	 *
+	 * The default implementation just send the command name with no arguments.
+	 *
+	 * @param irccdctl the irccdctl instance
+	 * @param args the command line arguments and options
+	 * @return the JSON object to send to the daemon
+	 * @post the returned JSON value must be an object
+	 */
+	virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const;
+
+	/**
+	 * Execute the command in the daemon.
+	 *
+	 * The user can return an object with any properties to forward to the client. Irccd will automatically
+	 * add the command name and the appropriate status code.
+	 *
+	 * The default return an empty object which indicates success.
+	 *
+	 * If any exception is thrown from this function, it is forwarded to the client as error status.
+	 *
+	 * @param irccd the instance
+	 * @param request the JSON request
+	 * @return the response
+	 */
+	virtual json::Value exec(Irccd &irccd, const json::Value &request) const;
+
+	/**
+	 * What to do when receiving the response from irccd.
+	 *
+	 * This default implementation just check for an error string and shows it if any.
+	 * 
+	 * @param irccdctl the irccdctl instane
+	 * @param object the result
+	 */
+	virtual void result(Irccdctl &irccdctl, const json::Value &response) const;
+};
+
+/**
+ * @brief Option description for a command.
+ */
+class RemoteCommand::Option {
+private:
+	std::string m_id;
+	std::string m_simple;
+	std::string m_long;
+	std::string m_arg;
+	std::string m_description;
+
+public:
+	/**
+	 * Constructor an option description.
+	 *
+	 * Simple and long keys must not start with '-' or '--', they will be added automatically.
+	 *
+	 * If arg is not empty, the option takes an argument.
+	 *
+	 * @pre id must not be empty
+	 * @pre at least simpleKey or longKey must not be empty
+	 * @pre description must not be empty
+	 * @param key the key the option key
+	 * @param description the description
+	 * @param flags the optional flags
+	 */
+	inline Option(std::string id,
+		      std::string simpleKey,
+		      std::string longKey,
+		      std::string arg,
+		      std::string description) noexcept
+		: m_id(std::move(id))
+		, m_simple(std::move(simpleKey))
+		, m_long(std::move(longKey))
+		, m_arg(std::move(arg))
+		, m_description(std::move(description))
+	{
+		assert(!m_id.empty());
+		assert(!m_simple.empty() || !m_long.empty());
+		assert(!m_description.empty());
+	}
+
+	/**
+	 * Get the id.
+	 *
+	 * @return the id
+	 */
+	inline const std::string &id() const noexcept
+	{
+		return m_id;
+	}
+
+	/**
+	 * Get the option key.
+	 *
+	 * @return the key
+	 */
+	inline const std::string &simpleKey() const noexcept
+	{
+		return m_simple;
+	}
+
+	/**
+	 * Get the long option.
+	 *
+	 * @return the long option
+	 */
+	inline const std::string &longKey() const noexcept
+	{
+		return m_long;
+	}
+
+	/**
+	 * Get the option description.
+	 *
+	 * @return the description
+	 */
+	inline const std::string &description() const noexcept
+	{
+		return m_description;
+	}
+
+	/**
+	 * Get the option argument name.
+	 *
+	 * @return the argument name if any
+	 */
+	inline const std::string &arg() const noexcept
+	{
+		return m_arg;
+	}
+};
+
+/**
+ * @brief Argument description for command.
+ */
+class RemoteCommand::Arg {
+private:
+	std::string m_name;
+	bool m_required;
+
+public:
+	/**
+	 * Construct an argument.
+	 *
+	 * @param name the name
+	 * @param required true if the argument is required
+	 */
+	inline Arg(std::string name, bool required) noexcept
+		: m_name(std::move(name))
+		, m_required(required)
+	{
+	}
+
+	/**
+	 * Get the argument name.
+	 *
+	 * @return the name
+	 */
+	inline const std::string &name() const noexcept
+	{
+		return m_name;
+	}
+
+	/**
+	 * Tells if the argument is required.
+	 *
+	 * @return true if required
+	 */
+	inline bool required() const noexcept
+	{
+		return m_required;
+	}
+};
+
+} // !irccd
+
+#endif // !IRCCD_COMMAND_H
--- a/lib/irccd/command/CMakeSources.cmake	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-set(
-	COMMAND_HEADERS
-	${CMAKE_CURRENT_LIST_DIR}/command.h
-	${CMAKE_CURRENT_LIST_DIR}/help.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin-info.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin-list.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin-load.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin-reload.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin-unload.h
-	${CMAKE_CURRENT_LIST_DIR}/server-cmode.h
-	${CMAKE_CURRENT_LIST_DIR}/server-cnotice.h
-	${CMAKE_CURRENT_LIST_DIR}/server-connect.h
-	${CMAKE_CURRENT_LIST_DIR}/server-disconnect.h
-	${CMAKE_CURRENT_LIST_DIR}/server-info.h
-	${CMAKE_CURRENT_LIST_DIR}/server-invite.h
-	${CMAKE_CURRENT_LIST_DIR}/server-join.h
-	${CMAKE_CURRENT_LIST_DIR}/server-kick.h
-	${CMAKE_CURRENT_LIST_DIR}/server-list.h
-	${CMAKE_CURRENT_LIST_DIR}/server-me.h
-	${CMAKE_CURRENT_LIST_DIR}/server-message.h
-	${CMAKE_CURRENT_LIST_DIR}/server-mode.h
-	${CMAKE_CURRENT_LIST_DIR}/server-nick.h
-	${CMAKE_CURRENT_LIST_DIR}/server-notice.h
-	${CMAKE_CURRENT_LIST_DIR}/server-part.h
-	${CMAKE_CURRENT_LIST_DIR}/server-reconnect.h
-	${CMAKE_CURRENT_LIST_DIR}/server-topic.h
-	${CMAKE_CURRENT_LIST_DIR}/watch.h
-)
-
-set(
-	COMMAND_SOURCES
-	${CMAKE_CURRENT_LIST_DIR}/command.cpp
-	${CMAKE_CURRENT_LIST_DIR}/help.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin-info.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin-list.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin-load.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin-reload.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin-unload.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-cmode.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-cnotice.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-connect.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-disconnect.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-info.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-invite.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-join.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-kick.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-list.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-me.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-message.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-mode.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-nick.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-notice.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-part.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-reconnect.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server-topic.cpp
-	${CMAKE_CURRENT_LIST_DIR}/watch.cpp
-)
--- a/lib/irccd/command/command.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * command.cpp -- remote command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <algorithm>
-#include <iomanip>
-#include <sstream>
-
-#include <irccd/logger.h>
-#include <irccd/system.h>
-
-#include "command.h"
-
-using namespace std::string_literals;
-
-namespace irccd {
-
-std::string RemoteCommand::usage() const
-{
-	std::ostringstream oss;
-
-	oss << "usage: " << sys::programName() << " " << m_name;
-
-	/* Options summary */
-	if (options().size() > 0)
-		oss << " [options...]";
-
-	/* Arguments summary */
-	if (args().size() > 0) {
-		oss << " ";
-
-		for (const auto &arg : args())
-			oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " ";
-	}
-
-	/* Description */
-	oss << "\n\n" << help() << "\n\n";
-
-	/* Options */
-	if (options().size() > 0) {
-		oss << "Options:\n";
-
-		for (const auto &opt : options()) {
-			std::ostringstream optoss;
-
-			/* Construct the line for the option in a single string to pad it correctly */
-			optoss << "  ";
-			optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : "   ");
-			optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : "");
-			optoss << opt.arg();
-
-			/* Add it padded with spaces */
-			oss << std::left << std::setw(28) << optoss.str();
-			oss << opt.description() << "\n";
-		}
-	}
-
-	return oss.str();
-}
-
-unsigned RemoteCommand::min() const noexcept
-{
-	auto list = args();
-
-	return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned {
-		return i + (arg.required() ? 1 : 0);
-	});
-}
-
-unsigned RemoteCommand::max() const noexcept
-{
-	return (unsigned)args().size();
-}
-
-json::Value RemoteCommand::request(Irccdctl &, const RemoteCommandRequest &) const
-{
-	return json::object({});
-}
-
-json::Value RemoteCommand::exec(Irccd &, const json::Value &) const
-{
-	return json::object({});
-}
-
-void RemoteCommand::result(Irccdctl &, const json::Value &response) const
-{
-	auto it = response.find("error");
-
-	if (it != response.end() && it->isString())
-		log::warning() << "irccdctl: " << it->toString() << std::endl;
-}
-
-} // !irccd
--- a/lib/irccd/command/command.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,469 +0,0 @@
-/*
- * command.h -- remote command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_COMMAND_H
-#define IRCCD_COMMAND_H
-
-#include <cassert>
-#include <map>
-#include <vector>
-
-#include <irccd/json.h>
-
-namespace irccd {
-
-class Irccd;
-class Irccdctl;
-
-/**
- * @brief Command line arguments to irccdctl.
- *
- * This class contains the resolved arguments from command line that can apply to the command.
- */
-class RemoteCommandRequest {
-public:
-	using Options = std::multimap<std::string, std::string>;
-	using Args = std::vector<std::string>;
-
-private:
-	Options m_options;
-	Args m_args;
-
-public:
-	/**
-	 * Construct the request.
-	 *
-	 * @param options the options
-	 * @param args the arguments
-	 */
-	inline RemoteCommandRequest(Options options, Args args) noexcept
-		: m_options(std::move(options))
-		, m_args(std::move(args))
-	{
-	}
-
-	/**
-	 * Get the arguments.
-	 *
-	 * @return the arguments
-	 */
-	inline const Args &args() const noexcept
-	{
-		return m_args;
-	}
-
-	/**
-	 * Get the options.
-	 *
-	 * @return the options
-	 */
-	inline const Options &options() const noexcept
-	{
-		return m_options;
-	}
-
-	/**
-	 * Get the number of arguments.
-	 *
-	 * @return the number of arguments
-	 */
-	inline unsigned length() const noexcept
-	{
-		return (unsigned)m_args.size();
-	}
-
-	/**
-	 * Check if the request has the given option id.
-	 *
-	 * @param option the option id
-	 * @return true if the option is available
-	 */
-	inline bool has(const std::string &option) const noexcept
-	{
-		return m_options.count(option) != 0;
-	}
-
-	/**
-	 * Get the argument at the specified index.
-	 *
-	 * @pre index < length()
-	 * @param index the argument index
-	 * @return the argument
-	 */
-	inline const std::string &arg(unsigned index) const noexcept
-	{
-		assert(index < m_args.size());
-
-		return m_args[index];
-	}
-
-	/**
-	 * Get the argument or default value if not available.
-	 *
-	 * @param index the index
-	 * @param defaultValue the value if index is out of range
-	 * @return the argument
-	 */
-	inline std::string argOr(unsigned index, std::string defaultValue) const noexcept
-	{
-		return index < m_args.size() ? m_args[index] : defaultValue;
-	}
-
-	/**
-	 * Get the given option by its id.
-	 *
-	 * @pre has(key)
-	 * @param key the option id
-	 * @return the option
-	 */
-	inline const std::string &option(const std::string &key) const noexcept
-	{
-		assert(m_options.count(key) != 0);
-
-		return m_options.find(key)->second;
-	}
-
-	/**
-	 * Get the given option by its id or defaultValue if not found.
-	 *
-	 * @param key the option id
-	 * @return the option
-	 */
-	inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept
-	{
-		auto it = m_options.find(key);
-
-		if (it == m_options.end())
-			return defaultValue;
-
-		return it->second;
-	}
-};
-
-/**
- * @brief Invokable command.
- *
- * A remote command is a invokable command in the irccd daemon. You can register dynamically any remote command you
- * like using Application::addCommand.
- *
- * The remote command will be usable directly from irccdctl without any other code.
- *
- * A remote command can have options and arguments. Options always come first, before arguments.
- *
- * The command workflow is defined as follow:
- *
- * 1. User wants to invoke a command, request() is called and return a JSON object containaing the request, it it send
- *    to the daemon.
- *
- * 2. The daemon receive the request and execute it using exec(). It returns a JSON object containint the request result
- *    or error if any.
- *
- * 3. Finally, the command receives the result in result() function and user can manipulate it. For convenience, the
- *    default implementation shows the error if any.
- */
-class RemoteCommand {
-public:
-	/**
-	 * @brief Defines available options for this command.
-	 */
-	class Option;
-
-	/**
-	 * @brief Defines available arguments for this command.
-	 */
-	class Arg;
-
-private:
-	std::string m_name;
-	std::string m_category;
-	bool m_visible;
-
-public:
-	inline RemoteCommand(std::string name, std::string category, bool visible = true) noexcept
-		: m_name(std::move(name))
-		, m_category(std::move(category))
-		, m_visible(visible)
-	{
-		assert(!m_name.empty());
-		assert(!m_category.empty());
-	}
-
-	/**
-	 * Default destructor virtual.
-	 */
-	virtual ~RemoteCommand() = default;
-
-	/**
-	 * Return the command name, must not have spaces.
-	 *
-	 * @return the command name
-	 */
-	inline const std::string &name() const noexcept
-	{
-		return m_name;
-	}
-
-	/**
-	 * Get the command category.
-	 *
-	 * Irccdctl will sort commands by categories.
-	 *
-	 * @return the category
-	 */
-	inline const std::string &category() const noexcept
-	{
-		return m_category;
-	}
-
-	/**
-	 * Hide the command in non-verbose mode.
-	 *
-	 * @return true if the command should be visible in non-verbose mode
-	 */
-	inline bool visible() const noexcept
-	{
-		return m_visible;
-	}
-
-	/**
-	 * Return the command documentation usage.
-	 *
-	 * @return the usage
-	 */
-	std::string usage() const;
-
-	/**
-	 * Return the help message.
-	 *
-	 * @return the help message
-	 */
-	virtual std::string help() const = 0;
-
-	/**
-	 * Get the supported irccdctl options.
-	 *
-	 * @return the options
-	 */
-	virtual std::vector<Option> options() const
-	{
-		return {};
-	}
-
-	/**
-	 * Get the supported arguments.
-	 *
-	 * @return the arguments
-	 */
-	virtual std::vector<Arg> args() const
-	{
-		return {};
-	}
-
-	/**
-	 * Get the minimum number of arguments required.
-	 *
-	 * @return the minimum
-	 */
-	unsigned min() const noexcept;
-
-	/**
-	 * Get the maximum number of arguments required.
-	 *
-	 * @return the maximum
-	 */
-	unsigned max() const noexcept;
-
-	/**
-	 * Prepare a JSON request to the daemon.
-	 *
-	 * If the command is local and does not need to send anything to irccd's instance, return a null JSON value.
-	 *
-	 * The default implementation just send the command name with no arguments.
-	 *
-	 * @param irccdctl the irccdctl instance
-	 * @param args the command line arguments and options
-	 * @return the JSON object to send to the daemon
-	 * @post the returned JSON value must be an object
-	 */
-	virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const;
-
-	/**
-	 * Execute the command in the daemon.
-	 *
-	 * The user can return an object with any properties to forward to the client. Irccd will automatically
-	 * add the command name and the appropriate status code.
-	 *
-	 * The default return an empty object which indicates success.
-	 *
-	 * If any exception is thrown from this function, it is forwarded to the client as error status.
-	 *
-	 * @param irccd the instance
-	 * @param request the JSON request
-	 * @return the response
-	 */
-	virtual json::Value exec(Irccd &irccd, const json::Value &request) const;
-
-	/**
-	 * What to do when receiving the response from irccd.
-	 *
-	 * This default implementation just check for an error string and shows it if any.
-	 * 
-	 * @param irccdctl the irccdctl instane
-	 * @param object the result
-	 */
-	virtual void result(Irccdctl &irccdctl, const json::Value &response) const;
-};
-
-/**
- * @brief Option description for a command.
- */
-class RemoteCommand::Option {
-private:
-	std::string m_id;
-	std::string m_simple;
-	std::string m_long;
-	std::string m_arg;
-	std::string m_description;
-
-public:
-	/**
-	 * Constructor an option description.
-	 *
-	 * Simple and long keys must not start with '-' or '--', they will be added automatically.
-	 *
-	 * If arg is not empty, the option takes an argument.
-	 *
-	 * @pre id must not be empty
-	 * @pre at least simpleKey or longKey must not be empty
-	 * @pre description must not be empty
-	 * @param key the key the option key
-	 * @param description the description
-	 * @param flags the optional flags
-	 */
-	inline Option(std::string id,
-		      std::string simpleKey,
-		      std::string longKey,
-		      std::string arg,
-		      std::string description) noexcept
-		: m_id(std::move(id))
-		, m_simple(std::move(simpleKey))
-		, m_long(std::move(longKey))
-		, m_arg(std::move(arg))
-		, m_description(std::move(description))
-	{
-		assert(!m_id.empty());
-		assert(!m_simple.empty() || !m_long.empty());
-		assert(!m_description.empty());
-	}
-
-	/**
-	 * Get the id.
-	 *
-	 * @return the id
-	 */
-	inline const std::string &id() const noexcept
-	{
-		return m_id;
-	}
-
-	/**
-	 * Get the option key.
-	 *
-	 * @return the key
-	 */
-	inline const std::string &simpleKey() const noexcept
-	{
-		return m_simple;
-	}
-
-	/**
-	 * Get the long option.
-	 *
-	 * @return the long option
-	 */
-	inline const std::string &longKey() const noexcept
-	{
-		return m_long;
-	}
-
-	/**
-	 * Get the option description.
-	 *
-	 * @return the description
-	 */
-	inline const std::string &description() const noexcept
-	{
-		return m_description;
-	}
-
-	/**
-	 * Get the option argument name.
-	 *
-	 * @return the argument name if any
-	 */
-	inline const std::string &arg() const noexcept
-	{
-		return m_arg;
-	}
-};
-
-/**
- * @brief Argument description for command.
- */
-class RemoteCommand::Arg {
-private:
-	std::string m_name;
-	bool m_required;
-
-public:
-	/**
-	 * Construct an argument.
-	 *
-	 * @param name the name
-	 * @param required true if the argument is required
-	 */
-	inline Arg(std::string name, bool required) noexcept
-		: m_name(std::move(name))
-		, m_required(required)
-	{
-	}
-
-	/**
-	 * Get the argument name.
-	 *
-	 * @return the name
-	 */
-	inline const std::string &name() const noexcept
-	{
-		return m_name;
-	}
-
-	/**
-	 * Tells if the argument is required.
-	 *
-	 * @return true if required
-	 */
-	inline bool required() const noexcept
-	{
-		return m_required;
-	}
-};
-
-} // !irccd
-
-#endif // !IRCCD_COMMAND_H
--- a/lib/irccd/command/help.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * command-help.cpp -- implementation of irccdctl help
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccdctl.h>
-#include <irccd/logger.h>
-
-#include "help.h"
-
-namespace irccd {
-
-namespace command {
-
-Help::Help()
-	: RemoteCommand("help", "General")
-{
-}
-
-std::vector<RemoteCommand::Arg> Help::args() const
-{
-	return {{ "command", true }};
-}
-
-std::string Help::help() const
-{
-	return "Get help about a command.";
-}
-
-json::Value Help::request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const
-{
-	auto it = irccdctl.commands().find(args.arg(0U));
-
-	if (it == irccdctl.commands().end())
-		log::warning() << "there is no command named: " << args.arg(0U) << std::endl;
-	else
-		log::warning() << it->second->usage() << std::flush;
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/help.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * command-help.h -- implementation of irccdctl help
- *
- * 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 _IRCCDCTL_COMMAND_HELP_H_
-#define _IRCCDCTL_COMMAND_HELP_H_
-
-/**
- * @file command-help.h
- * @brief Implementation of irccdctl help.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class Help
- * @brief Implementation of irccdctl help.
- */
-class Help : public RemoteCommand {
-public:
-	Help();
-
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- a/lib/irccd/command/plugin-info.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * plugin-info.cpp -- implementation of plugin-info command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include <irccd-config.h>
-
-#include <irccd/irccd.h>
-#include <irccd/command/plugin-info.h>
-
-namespace irccd {
-
-namespace command {
-
-PluginInfo::PluginInfo()
-	: RemoteCommand("plugin-info", "Plugins")
-{
-}
-
-std::string PluginInfo::help() const
-{
-	return "Get plugin information.";
-}
-
-std::vector<RemoteCommand::Arg> PluginInfo::args() const
-{
-	return {{ "plugin", true }};
-}
-
-json::Value PluginInfo::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({{ "plugin", args.arg(0) }});
-}
-
-json::Value PluginInfo::exec(Irccd &irccd, const json::Value &request) const
-{
-#if defined(WITH_JS)
-	auto plugin = irccd.requirePlugin(request.at("plugin").toString());
-	
-	return json::object({
-		{ "author",	plugin->info().author	},
-		{ "license",	plugin->info().license	},
-		{ "summary",	plugin->info().summary	},
-		{ "version",	plugin->info().version	}
-	});
-#else
-	(void)irccd;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-void PluginInfo::result(Irccdctl &irccdctl, const json::Value &result) const
-{
-	RemoteCommand::result(irccdctl, result);
-
-	/* Plugin information */
-	if (result.valueOr("status", false).toBool()) {
-		std::cout << std::boolalpha;
-		std::cout << "Author         : " << result.valueOr("author", "").toString(true) << std::endl;
-		std::cout << "License        : " << result.valueOr("license", "").toString(true) << std::endl;
-		std::cout << "Summary        : " << result.valueOr("summary", "").toString(true) << std::endl;
-		std::cout << "Version        : " << result.valueOr("version", "").toString(true) << std::endl;
-	}
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/command/plugin-info.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * plugin-info.h -- implementation of plugin-info command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_PLUGIN_INFO_H_
-#define _IRCCD_PLUGIN_INFO_H_
-
-/**
- * @file plugin-info.h
- * @brief Implementation of plugin-info transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginInfo
- * @brief Implementation of plugin-info transport command.
- */
-class PluginInfo : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	PluginInfo();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-
-	/**
-	 * @copydoc RemoteCommand::result
-	 */
-	void result(Irccdctl &irccdctl, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_PLUGIN_INFO_H_
--- a/lib/irccd/command/plugin-list.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * command-plugin-list.cpp -- implementation of plugin-list transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include <irccd-config.h>
-
-#include <irccd/irccd.h>
-#include <irccd/command/plugin-list.h>
-
-namespace irccd {
-
-namespace command {
-
-PluginList::PluginList()
-	: RemoteCommand("plugin-list", "Plugins")
-{
-}
-
-std::string PluginList::help() const
-{
-	return "Get the list of loaded plugins.";
-}
-
-json::Value PluginList::exec(Irccd &irccd, const json::Value &request) const
-{
-#if defined(WITH_JS)
-	json::Value response = RemoteCommand::exec(irccd, request);
-	json::Value list = json::array({});
-
-	for (const auto &plugin : irccd.plugins())
-		list.append(plugin.first);
-
-	response.insert("list", std::move(list));
-
-	return response;
-#else
-	(void)irccd;
-	(void)tc;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-void PluginList::result(Irccdctl &irccdctl, const json::Value &object) const
-{
-	RemoteCommand::result(irccdctl, object);
-
-	for (const auto &n : object.valueOr("list", json::Type::Array, json::array({})))
-		std::cout << n.toString() << std::endl;
-}
-
-} // !command
-
-}
--- a/lib/irccd/command/plugin-list.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * command-plugin-list.h -- implementation of plugin-list transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_PLUGIN_LIST_H_
-#define _IRCCD_COMMAND_PLUGIN_LIST_H_
-
-/**
- * @file command-plugin-list.h
- * @brief Implementation of plugin-list transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginList
- * @brief Implementation of plugin-list transport command.
- */
-class PluginList : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	PluginList();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-
-	/**
-	 * @copydoc RemoteCommand::result
-	 */
-	void result(Irccdctl &irccdctl, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_LIST_H_
--- a/lib/irccd/command/plugin-load.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * command-plugin-load.cpp -- implementation of plugin-load transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd-config.h>
-#include <irccd/irccd.h>
-#include <irccd/command/plugin-load.h>
-
-namespace irccd {
-
-namespace command {
-
-PluginLoad::PluginLoad()
-	: RemoteCommand("plugin-load", "Plugins")
-{
-}
-
-std::string PluginLoad::help() const
-{
-	return "Load a plugin.";
-}
-
-std::vector<RemoteCommand::Arg> PluginLoad::args() const
-{
-	return {{ "plugin", true }};
-}
-
-json::Value PluginLoad::exec(Irccd &irccd, const json::Value &request) const
-{
-#if defined(WITH_JS)
-	auto name = request.at("plugin").toString();
-
-	irccd.loadPlugin(name, name, true);
-
-	return RemoteCommand::exec(irccd, request);
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/plugin-load.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * command-plugin-load.h -- implementation of plugin-load transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_PLUGIN_LOAD_H_
-#define _IRCCD_COMMAND_PLUGIN_LOAD_H_
-
-/**
- * @file plugin-load.h
- * @brief Implementation of plugin-load transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginLoad
- * @brief Implementation of plugin-load transport command.
- */
-class PluginLoad : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	PluginLoad();
-
-	/**
-	 * @copydoc TransportCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_LOAD_H_
--- a/lib/irccd/command/plugin-reload.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * command-plugin-reload.cpp -- implementation of plugin-reload transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd-config.h>
-#include <irccd/irccd.h>
-#include <irccd/command/plugin-reload.h>
-
-namespace irccd {
-
-namespace command {
-
-PluginReload::PluginReload()
-	: RemoteCommand("plugin-reload", "Plugins")
-{
-}
-
-std::string PluginReload::help() const
-{
-	return "Reload a plugin.";
-}
-
-std::vector<RemoteCommand::Arg> PluginReload::args() const
-{
-	return {{ "plugin", true }};
-}
-
-json::Value PluginReload::exec(Irccd &irccd, const json::Value &request) const
-{
-#if defined(WITH_JS)
-	irccd.requirePlugin(request.at("plugin").toString())->onReload();
-
-	return RemoteCommand::exec(irccd, request);
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/plugin-reload.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * plugin-reload.h -- implementation of plugin-reload transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_PLUGIN_RELOAD_H_
-#define _IRCCD_COMMAND_PLUGIN_RELOAD_H_
-
-/**
- * @file command-plugin-load.h
- * @brief Implementation of plugin-reload transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginReload
- * @brief Implementation of plugin-reload transport command.
- */
-class PluginReload : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	PluginReload();
-
-	/**
-	 * @copydoc TransportCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_RELOAD_H_
--- a/lib/irccd/command/plugin-unload.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * plugin-unload.cpp -- implementation of plugin-unload transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd-config.h>
-#include <irccd/irccd.h>
-#include <irccd/command/plugin-unload.h>
-
-namespace irccd {
-
-namespace command {
-
-PluginUnload::PluginUnload()
-	: RemoteCommand("plugin-unload", "Plugins")
-{
-}
-
-std::string PluginUnload::help() const
-{
-	return "Unload a plugin.";
-}
-
-std::vector<RemoteCommand::Arg> PluginUnload::args() const
-{
-	return {{ "plugin", true }};
-}
-
-json::Value PluginUnload::exec(Irccd &irccd, const json::Value &request) const
-{
-#if defined(WITH_JS)
-	irccd.unloadPlugin(request.at("plugin").toString());
-
-	return RemoteCommand::exec(irccd, request);
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/plugin-unload.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * plugin-unload.h -- implementation of plugin-unload transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_PLUGIN_UNLOAD_H_
-#define _IRCCD_COMMAND_PLUGIN_UNLOAD_H_
-
-/**
- * @file plugin-unload.h
- * @brief Implementation of plugin-unload transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginUnload
- * @brief Implementation of plugin-unload transport command.
- */
-class PluginUnload : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	PluginUnload();
-
-	/**
-	 * @copydoc TransportCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_UNLOAD_H_
--- a/lib/irccd/command/server-cmode.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * server-cmode.cpp -- implementation of server-cmode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-#include <irccd/command/server-cmode.h>
-
-namespace irccd {
-
-namespace command {
-
-ServerChannelMode::ServerChannelMode()
-	: RemoteCommand("server-cmode", "Server")
-{
-}
-
-std::string ServerChannelMode::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerChannelMode::args() const
-{
-	return {
-		{ "server", true },
-		{ "channel", true },
-		{ "mode", true }
-	};
-}
-
-json::Value ServerChannelMode::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->cmode(
-		request.at("channel").toString(),
-		request.at("mode").toString()
-	);
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/command/server-cmode.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * server-cmode.h -- implementation of server-cmode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
-#define _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
-
-/**
- * @file server-cmode.h
- * @brief Implementation of server-cmode transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerChannelMode
- * @brief Implementation of server-cmode transport command.
- */
-class ServerChannelMode : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	ServerChannelMode();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-	 
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
--- a/lib/irccd/command/server-cnotice.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * server-cnotice.cpp -- implementation of server-cnotice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-cnotice.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerChannelNotice::ServerChannelNotice()
-	: RemoteCommand("server-cnotice", "Server")
-{
-}
-
-std::string ServerChannelNotice::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerChannelNotice::args() const
-{
-	return {
-		{ "server", true },
-		{ "channel", true },
-		{ "message", true }
-	};
-}
-
-json::Value ServerChannelNotice::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->cnotice(
-		request.at("channel").toString(),
-		request.at("message").toString()
-	);
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-cnotice.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * server-cnotice.h -- implementation of server-cnotice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_CNOTICE_H_
-#define _IRCCD_COMMAND_SERVER_CNOTICE_H_
-
-/**
- * @file server-cnotice.h
- * @brief Implementation of server-cnotice transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerChannelNotice
- * @brief Implementation of server-cnotice transport command.
- *
- * Send a channel notice to the specified channel.
- *
- * {
- *   "command": "server-cnotice",
- *   "server": "the server name",
- *   "channel": "name",
- *   "message": "the message"
- * }
- */
-class ServerChannelNotice : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	ServerChannelNotice();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CNOTICE_H_
--- a/lib/irccd/command/server-connect.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/*
- * command-server-connect.cpp -- implementation of server-connect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <limits>
-
-#include <irccd/irccd.h>
-#include <irccd/server.h>
-#include <irccd/util.h>
-
-#include "server-connect.h"
-
-namespace irccd {
-
-namespace command {
-
-namespace {
-
-std::string readInfoName(const json::Value &object)
-{
-	auto it = object.find("name");
-
-	if (it == object.end())
-		throw std::invalid_argument("missing 'name' property");
-	if (!it->isString() || !util::isIdentifierValid(it->toString()))
-		throw std::invalid_argument("invalid server name");
-
-	return it->toString();
-}
-
-std::string readInfoHost(const json::Value &object)
-{
-	auto it = object.find("host");
-
-	if (it == object.end())
-		throw std::invalid_argument("missing 'host' property");
-	if (!it->isString())
-		throw std::invalid_argument("invalid host");
-
-	return it->toString();
-}
-
-std::uint16_t readInfoPort(const json::Value &object)
-{
-	auto it = object.find("port");
-	uint16_t port = 6667;
-
-	if (it != object.end())
-		if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max())
-			port = static_cast<std::uint16_t>(it->toInt());
-
-	return port;
-}
-
-ServerInfo readInfo(const json::Value &object)
-{
-	ServerInfo info;
-
-	/* Mandatory */
-	info.name = readInfoName(object);
-	info.host = readInfoHost(object);
-
-	/* Optional */
-	info.port = readInfoPort(object);
-
-	if (object.valueOr("ssl", json::Type::Boolean, false).toBool())
-#if defined(WITH_SSL)
-		info.flags |= ServerInfo::Ssl;
-#else
-		throw std::invalid_argument("ssl is disabled");
-#endif
-
-	if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool())
-		info.flags |= ServerInfo::SslVerify;
-
-	return info;
-}
-
-ServerIdentity readIdentity(const json::Value &object)
-{
-	ServerIdentity identity;
-
-	identity.nickname = object.valueOr("nickname", json::Type::String, identity.nickname).toString();
-	identity.realname = object.valueOr("realname", json::Type::String, identity.realname).toString();
-	identity.username = object.valueOr("username", json::Type::String, identity.username).toString();
-	identity.ctcpversion = object.valueOr("ctcpVersion", json::Type::String, identity.ctcpversion).toString();
-
-	return identity;
-}
-
-ServerSettings readSettings(const json::Value &object)
-{
-	ServerSettings settings;
-
-	settings.command = object.valueOr("commandChar", json::Type::String, settings.command).toString();
-	settings.recotries = object.valueOr("reconnectTries", json::Type::Int, settings.recotries).toInt();
-	settings.recotimeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.recotimeout).toInt();
-
-	return settings;
-}
-
-} // !namespace
-
-ServerConnect::ServerConnect()
-	: RemoteCommand("server-connect", "Server")
-{
-}
-
-std::string ServerConnect::help() const
-{
-	return "Connect to a server.";
-}
-
-std::vector<RemoteCommand::Option> ServerConnect::options() const
-{
-	return {
-		{ "command", "c", "command", "char", "command character to use" },
-		{ "nickname", "n", "nickname", "nickname", "nickname to use" },
-		{ "realname", "r", "realname", "realname", "realname to use" },
-		{ "sslverify", "S", "ssl-verify", "", "verify SSL" },
-		{ "ssl", "s", "ssl", "", "connect with SSL" },
-		{ "username", "u", "username", "", "username to use" },
-	};
-}
-
-std::vector<RemoteCommand::Arg> ServerConnect::args() const
-{
-	return {
-		{ "id", true },
-		{ "host", true },
-		{ "port", false }
-	};
-}
-
-json::Value ServerConnect::exec(Irccd &irccd, const json::Value &request) const
-{
-	auto server = std::make_shared<Server>(readInfo(request), readIdentity(request), readSettings(request));
-
-	if (irccd.hasServer(server->info().name))
-		throw std::invalid_argument("server '" + server->info().name + "' already exists");
-
-	irccd.addServer(std::move(server));
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-connect.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * server-connect.h -- implementation of server-connect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_CONNECT_H_
-#define _IRCCD_COMMAND_SERVER_CONNECT_H_
-
-/**
- * @file server-connect.h
- * @brief Implementation of server-connect transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerConnect
- * @brief Implementation of server-connect transport command.
- */
-class ServerConnect : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	ServerConnect();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::options
-	 */
-	std::vector<Option> options() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CONNECT_H_
--- a/lib/irccd/command/server-disconnect.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * command-server-disconnect.cpp -- implementation of server-disconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-disconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerDisconnect::ServerDisconnect()
-	: RemoteCommand("server-disconnect", "Server")
-{
-}
-
-std::string ServerDisconnect::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerDisconnect::args() const
-{
-	return {{ "server", false }};
-}
-
-json::Value ServerDisconnect::exec(Irccd &irccd, const json::Value &request) const
-{
-	auto it = request.find("server");
-
-	if (it == request.end())
-		irccd.clearServers();
-	else
-		irccd.removeServer(it->toString());
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-disconnect.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * server-disconnect.h -- implementation of server-disconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_DISCONNECT_H_
-#define _IRCCD_COMMAND_SERVER_DISCONNECT_H_
-
-/**
- * @file server-disconnect.h
- * @brief Implementation of server-disconnect transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerDisconnect
- * @brief Implementation of server-disconnect transport command.
- */
-class ServerDisconnect : public RemoteCommand {
-public:
-	/**
-	 * Constructor.
-	 */
-	ServerDisconnect();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_DISCONNECT_H_
--- a/lib/irccd/command/server-info.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
- * command-server-info.cpp -- implementation of server-info transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include <irccd/irccd.h>
-
-#include "server-info.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerInfo::ServerInfo()
-	: RemoteCommand("server-info", "Server")
-{
-}
-
-std::string ServerInfo::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerInfo::args() const
-{
-	return {{ "server", true }};
-}
-
-json::Value ServerInfo::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server",	args.args()[0]		},
-		{ "target",	args.args()[1]		},
-		{ "channel",	args.args()[2]		}
-	});
-}
-
-json::Value ServerInfo::exec(Irccd &irccd, const json::Value &request) const
-{
-	auto server = irccd.requireServer(request.at("server").toString());
-	auto response = RemoteCommand::exec(irccd, request);
-
-	/* General stuff */
-	response.insert("name", server->info().name);
-	response.insert("host", server->info().host);
-	response.insert("port", server->info().port);
-	response.insert("nickname", server->identity().nickname);
-	response.insert("username", server->identity().username);
-	response.insert("realname", server->identity().realname);
-
-	/* Optional stuff */
-	if (server->info().flags & irccd::ServerInfo::Ipv6)
-		response.insert("ipv6", true);
-	if (server->info().flags & irccd::ServerInfo::Ssl)
-		response.insert("ssl", true);
-	if (server->info().flags & irccd::ServerInfo::SslVerify)
-		response.insert("sslVerify", true);
-
-	/* Channel list */
-	auto channels = json::array({});
-
-	for (const auto &c : server->settings().channels)
-		channels.append(c.name);
-
-	response.insert("channels", std::move(channels));
-
-	return response;
-}
-
-void ServerInfo::result(Irccdctl &irccdctl, const json::Value &response) const
-{
-	RemoteCommand::result(irccdctl, response);
-
-	/* Server information */
-	std::cout << std::boolalpha;
-	std::cout << "Name           : " << response.valueOr("name", "").toString(true) << std::endl;
-	std::cout << "Host           : " << response.valueOr("host", "").toString(true) << std::endl;
-	std::cout << "Port           : " << response.valueOr("port", "").toString(true) << std::endl;
-	std::cout << "Ipv6           : " << response.valueOr("ipv6", "").toString(true) << std::endl;
-	std::cout << "SSL            : " << response.valueOr("ssl", "").toString(true) << std::endl;
-	std::cout << "SSL verified   : " << response.valueOr("sslVerify", "").toString(true) << std::endl;
-
-	/* Channels */
-	std::cout << "Channels       : ";
-
-	for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({})))
-		std::cout << v.toString() << " ";
-
-	std::cout << std::endl;
-
-	/* Identity */
-	std::cout << "Nickname       : " << response.valueOr("nickname", "").toString(true) << std::endl;
-	std::cout << "User name      : " << response.valueOr("username", "").toString(true) << std::endl;
-	std::cout << "Real name      : " << response.valueOr("realname", "").toString(true) << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-info.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * command-server-info.h -- implementation of server-info transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_INFO_H_
-#define _IRCCD_COMMAND_SERVER_INFO_H_
-
-/**
- * @file command-server-info.h
- * @brief Implementation of server-info transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInfo
- * @brief Implementation of server-info transport command.
- */
-class ServerInfo : public RemoteCommand {
-public:
-	ServerInfo();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc TransportCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &object) const override;
-
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_INFO_H_
--- a/lib/irccd/command/server-invite.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * command-server-invite.cpp -- implementation of server-invite transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-invite.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerInvite::ServerInvite()
-	: RemoteCommand("server-invite", "Server")
-{
-}
-
-std::string ServerInvite::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerInvite::args() const
-{
-	return {
-		{ "server", true },
-		{ "nickname", true },
-		{ "channel", true }
-	};
-}
-
-json::Value ServerInvite::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server",	args.args()[0]		},
-		{ "target",	args.args()[1]		},
-		{ "channel",	args.args()[2]		}
-	});
-}
-
-json::Value ServerInvite::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(
-		request.at("server").toString())->invite(
-		request.at("target").toString(),
-		request.at("channel").toString()
-	);
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/command/server-invite.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * server-invite.h -- implementation of server-invite transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_INVITE_H_
-#define _IRCCD_COMMAND_SERVER_INVITE_H_
-
-/**
- * @file server-invite.h
- * @brief Implementation of server-invite transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInvite
- * @brief Implementation of server-invite transport command.
- */
-class ServerInvite : public RemoteCommand {
-public:
-	ServerInvite();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-
-	
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_INVITE_H_
--- a/lib/irccd/command/server-join.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * server-join.cpp -- implementation of server-join transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-join.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerJoin::ServerJoin()
-	: RemoteCommand("server-join", "Server")
-{
-}
-
-std::string ServerJoin::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerJoin::args() const
-{
-	return {
-		{ "server", true },
-		{ "channel", true },
-		{ "password", false }
-	};
-}
-
-json::Value ServerJoin::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	auto req = json::object({
-		{ "server", args.args()[0] },
-		{ "channel", args.args()[1] }
-	});
-
-	if (args.length() == 3)
-		req.insert("password", args.args()[2]);
-
-	return req;
-}
-
-json::Value ServerJoin::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(
-		request.at("server").toString())->join(
-		request.at("channel").toString(),
-		request.valueOr("password", json::Type::String, "").toString()
-	);
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-join.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-join.h -- implementation of server-join transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_JOIN_H_
-#define _IRCCD_COMMAND_SERVER_JOIN_H_
-
-/**
- * @file server-join.h
- * @brief Implementation of server-join transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerJoin
- * @brief Implementation of server-join transport command.
- */
-class ServerJoin : public RemoteCommand {
-public:
-	ServerJoin();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_JOIN_H_
--- a/lib/irccd/command/server-kick.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * command-server-kick.cpp -- implementation of server-kick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-kick.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerKick::ServerKick()
-	: RemoteCommand("server-kick", "Server")
-{
-}
-
-std::string ServerKick::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerKick::args() const
-{
-	return {
-		{ "server", true },
-		{ "target", true },
-		{ "channel", true },
-		{ "reason", false }
-	};
-}
-
-json::Value ServerKick::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	auto req = json::object({
-		{ "server", args.arg(0) },
-		{ "target", args.arg(1) },
-		{ "channel", args.arg(2) }
-	});
-
-	if (args.length() == 4)
-		req.insert("reason", args.arg(3));
-
-	return req;
-}
-
-json::Value ServerKick::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->kick(
-		request.at("target").toString(),
-		request.at("channel").toString(),
-		request.valueOr("reason", json::Type::String, "").toString()
-	);
-
-	return RemoteCommand::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-kick.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-kick.h -- implementation of server-kick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_KICK_H_
-#define _IRCCD_COMMAND_SERVER_KICK_H_
-
-/**
- * @file server-kick.h
- * @brief Implementation of server-kick transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerKick
- * @brief Implementation of server-kick transport command.
- */
-class ServerKick : public RemoteCommand {
-public:
-	ServerKick();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_KICK_H_
--- a/lib/irccd/command/server-list.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * server-list.cpp -- implementation of server-list transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include <irccd/irccd.h>
-
-#include "server-list.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerList::ServerList()
-	: RemoteCommand("server-list", "Server")
-{
-}
-
-std::string ServerList::help() const
-{
-	return "";
-}
-
-json::Value ServerList::exec(Irccd &irccd, const json::Value &) const
-{
-	auto json = json::object({});
-	auto list = json::array({});
-
-	for (const auto &pair : irccd.servers())
-		list.append(pair.first);
-
-	json.insert("list", std::move(list));
-
-	return json;
-}
-
-void ServerList::result(Irccdctl &, const json::Value &response) const
-{
-	for (const auto &n : response.valueOr("list", json::Type::Array, json::array({})))
-		std::cout << n.toString() << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-list.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * server-list.h -- implementation of server-list transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_LIST_H_
-#define _IRCCD_COMMAND_SERVER_LIST_H_
-
-/**
- * @file command-server-list.h
- * @brief Implementation of server-list transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerList
- * @brief Implementation of server-list transport command.
- */
-class ServerList : public RemoteCommand {
-public:
-	ServerList();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_LIST_H_
--- a/lib/irccd/command/server-me.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * server-me.cpp -- implementation of server-me transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-me.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerMe::ServerMe()
-	: RemoteCommand("server-me", "Server")
-{
-}
-
-std::string ServerMe::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerMe::args() const
-{
-	return {
-		{ "server", true },
-		{ "target", true },
-		{ "message", true }
-	};
-}
-
-json::Value ServerMe::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "target", args.arg(1) },
-		{ "message", args.arg(2) }
-	});
-}
-
-json::Value ServerMe::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->me(
-		request.at("target").toString(),
-		request.at("message").toString()
-	);
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-me.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-me.h -- implementation of server-me transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_ME_H_
-#define _IRCCD_COMMAND_SERVER_ME_H_
-
-/**
- * @file command-.h
- * @brief Implementation of  transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMe
- * @brief Implementation of server-me transport command.
- */
-class ServerMe : public RemoteCommand {
-public:
-	ServerMe();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_ME_H_
--- a/lib/irccd/command/server-message.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * server-message.cpp -- implementation of server-message transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-message.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerMessage::ServerMessage()
-	: RemoteCommand("server-message", "Server")
-{
-}
-
-std::string ServerMessage::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerMessage::args() const
-{
-	return {
-		{ "server", true },
-		{ "target", true },
-		{ "message", true }
-	};
-}
-
-json::Value ServerMessage::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "target", args.arg(1) },
-		{ "message", args.arg(2) }
-	});
-}
-
-json::Value ServerMessage::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->me(
-		request.at("target").toString(),
-		request.at("message").toString()
-	);
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-message.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-message.h -- implementation of server-message transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_MESSAGE_H_
-#define _IRCCD_COMMAND_SERVER_MESSAGE_H_
-
-/**
- * @file command-server-message.h
- * @brief Implementation of server-message transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMessage
- * @brief Implementation of server-message transport command.
- */
-class ServerMessage : public RemoteCommand {
-public:
-	ServerMessage();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_MESSAGE_H_
--- a/lib/irccd/command/server-mode.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * command-server-mode.cpp -- implementation of server-mode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-mode.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerMode::ServerMode()
-	: RemoteCommand("server-mode", "Server")
-{
-}
-
-std::string ServerMode::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerMode::args() const
-{
-	return {
-		{ "server", true },
-		{ "mode", true }
-	};
-}
-
-json::Value ServerMode::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "mode", args.arg(1) }
-	});
-}
-
-json::Value ServerMode::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->mode(request.at("mode").toString());
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-mode.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-mode.h -- implementation of server-mode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_MODE_H_
-#define _IRCCD_COMMAND_SERVER_MODE_H_
-
-/**
- * @file server-mode.h
- * @brief Implementation of server-mode transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMode
- * @brief Implementation of server-mode transport command.
- */
-class ServerMode : public RemoteCommand {
-public:
-	ServerMode();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_MODE_H_
--- a/lib/irccd/command/server-nick.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * server-nick.cpp -- implementation of server-nick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-nick.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerNick::ServerNick()
-	: RemoteCommand("server-nick", "Server")
-{
-}
-
-std::string ServerNick::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerNick::args() const
-{
-	return {
-		{ "server", true },
-		{ "nickname", true }
-	};
-}
-
-json::Value ServerNick::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "nickname", args.arg(1) }
-	});
-}
-
-json::Value ServerNick::exec(Irccd &irccd, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString());
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-nick.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-nick.h -- implementation of server-nick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_NICK_H_
-#define _IRCCD_COMMAND_SERVER_NICK_H_
-
-/**
- * @file command-server-nick.h
- * @brief Implementation of server-nick transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNick
- * @brief Implementation of server-nick transport command.
- */
-class ServerNick : public RemoteCommand {
-public:
-	ServerNick();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_NICK_H_
--- a/lib/irccd/command/server-notice.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * server-notice.cpp -- implementation of server-notice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-notice.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerNotice::ServerNotice()
-	: RemoteCommand("server-notice", "Server")
-{
-}
-
-std::string ServerNotice::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerNotice::args() const
-{
-	return {
-		{ "server", true },
-		{ "target", true },
-		{ "message", true }
-	};
-}
-
-json::Value ServerNotice::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "target", args.arg(1) },
-		{ "message", args.arg(2) }
-	});
-}
-
-json::Value ServerNotice::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->notice(
-		request.at("target").toString(),
-		request.at("message").toString()
-	);
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/command/server-notice.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-notice.h -- implementation of server-notice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_NOTICE_H_
-#define _IRCCD_COMMAND_SERVER_NOTICE_H_
-
-/**
- * @file server-notice.h
- * @brief Implementation of server-notice transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNotice
- * @brief Implementation of server-notice transport command.
- */
-class ServerNotice : public RemoteCommand {
-public:
-	ServerNotice();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_NOTICE_H_
--- a/lib/irccd/command/server-part.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * server-part.cpp -- implementation of server-part transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-part.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerPart::ServerPart()
-	: RemoteCommand("server-part", "Server")
-{
-}
-
-std::string ServerPart::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerPart::args() const
-{
-	return {
-		{ "server", true },
-		{ "channel", true },
-		{ "reason", false }
-	};
-}
-
-json::Value ServerPart::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	auto req = json::object({
-		{ "server", args.arg(0) },
-		{ "channel", args.arg(1) }
-	});
-
-	if (args.length() == 3)
-		req.insert("reason", args.arg(2));
-
-	return req;
-}
-
-json::Value ServerPart::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->part(
-		request.at("channel").toString(),
-		request.valueOr("reason", "").toString()
-	);
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-part.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-part.h -- implementation of server-part transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_PART_H_
-#define _IRCCD_COMMAND_SERVER_PART_H_
-
-/**
- * @file server-part.h
- * @brief Implementation of server-part transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerPart
- * @brief Implementation of server-part transport command.
- */
-class ServerPart : public RemoteCommand {
-public:
-	ServerPart();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_PART_H_
--- a/lib/irccd/command/server-reconnect.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * command-server-reconnect.cpp -- implementation of server-reconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-reconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerReconnect::ServerReconnect()
-	: RemoteCommand("server-reconnect", "Server")
-{
-}
-
-std::string ServerReconnect::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerReconnect::args() const
-{
-	return {{ "server", false }};
-}
-
-json::Value ServerReconnect::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return args.length() == 0 ? nullptr : json::object({ { "server", args.arg(0) } });
-}
-
-json::Value ServerReconnect::exec(Irccd &irccd, const json::Value &request) const
-{
-	auto server = request.find("server");
-
-	if (server != request.end() && server->isString())
-		irccd.requireServer(server->toString())->reconnect();
-	else
-		for (auto &pair : irccd.servers())
-			pair.second->reconnect();
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-reconnect.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-reconnect.h -- implementation of server-reconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_RECONNECT_H_
-#define _IRCCD_COMMAND_SERVER_RECONNECT_H_
-
-/**
- * @file server-reconnect.h
- * @brief Implementation of server-reconnect transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerReconnect
- * @brief Implementation of server-reconnect transport command.
- */
-class ServerReconnect : public RemoteCommand {
-public:
-	ServerReconnect();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_RECONNECT_H_
--- a/lib/irccd/command/server-topic.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * command-server-topic.cpp -- implementation of server-topic transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-
-#include "server-topic.h"
-
-namespace irccd {
-
-namespace command {
-
-ServerTopic::ServerTopic()
-	: RemoteCommand("server-topic", "Server")
-{
-}
-
-std::string ServerTopic::help() const
-{
-	return "";
-}
-
-std::vector<RemoteCommand::Arg> ServerTopic::args() const
-{
-	return {
-		{ "server", true },
-		{ "channel", true },
-		{ "topic", true }
-	};
-}
-
-json::Value ServerTopic::request(Irccdctl &, const RemoteCommandRequest &args) const
-{
-	return json::object({
-		{ "server", args.arg(0) },
-		{ "channel", args.arg(1) },
-		{ "topic", args.arg(2) }
-	});
-}
-
-json::Value ServerTopic::exec(Irccd &irccd, const json::Value &request) const
-{
-	irccd.requireServer(request.at("server").toString())->topic(
-		request.at("channel").toString(),
-		request.at("topic").toString()
-	);
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/server-topic.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * server-topic.h -- implementation of server-topic transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IRCCD_COMMAND_SERVER_TOPIC_H_
-#define _IRCCD_COMMAND_SERVER_TOPIC_H_
-
-/**
- * @file server-topic.h
- * @brief Implementation of server-topic transport command.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerTopic
- * @brief Implementation of server-topic transport command.
- */
-class ServerTopic : public RemoteCommand {
-public:
-	ServerTopic();
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::args
-	 */
-	std::vector<Arg> args() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
-
-	/**
-	 * @copydoc RemoteCommand::exec
-	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_TOPIC_H_
--- a/lib/irccd/command/watch.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-/*
- * command-watch.cpp -- implementation of irccdctl watch
- *
- * 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 <functional>
-#include <iostream>
-#include <sstream>
-#include <unordered_map>
-
-#include <irccd/irccdctl.h>
-
-#include "watch.h"
-
-namespace irccd {
-
-namespace command {
-
-namespace {
-
-void onChannelMode(const json::Value &v)
-{
-	std::cout << "event:       onChannelMode\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "mode:        " << v.valueOr("mode", "").toString() << "\n";
-	std::cout << "argument:    " << v.valueOr("argument", "").toString() << "\n";
-}
-
-void onChannelNotice(const json::Value &v)
-{
-	std::cout << "event:       onChannelNotice\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
-}
-
-void onConnect(const json::Value &v)
-{
-	std::cout << "event:       onConnect\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-}
-
-void onInvite(const json::Value &v)
-{
-	std::cout << "event:       onInvite\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-}
-
-void onJoin(const json::Value &v)
-{
-	std::cout << "event:       onJoin\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-}
-
-void onKick(const json::Value &v)
-{
-	std::cout << "event:       onKick\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "target:      " << v.valueOr("target", "").toString() << "\n";
-	std::cout << "reason:      " << v.valueOr("reason", "").toString() << "\n";
-}
-
-void onMessage(const json::Value &v)
-{
-	std::cout << "event:       onMessage\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
-}
-
-void onMe(const json::Value &v)
-{
-	std::cout << "event:       onMe\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "target:      " << v.valueOr("target", "").toString() << "\n";
-	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
-}
-
-void onMode(const json::Value &v)
-{
-	std::cout << "event:       onMode\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "mode:        " << v.valueOr("mode", "").toString() << "\n";
-}
-
-void onNames(const json::Value &v)
-{
-	std::cout << "event:       onNames\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "names:       " << v.valueOr("names", "").toJson(0) << "\n";
-}
-
-void onNick(const json::Value &v)
-{
-	std::cout << "event:       onNick\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "nickname:    " << v.valueOr("nickname", "").toString() << "\n";
-}
-
-void onNotice(const json::Value &v)
-{
-	std::cout << "event:       onNotice\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "message:      " << v.valueOr("message", "").toString() << "\n";
-}
-
-void onPart(const json::Value &v)
-{
-	std::cout << "event:       onPart\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "reason:      " << v.valueOr("reason", "").toString() << "\n";
-}
-
-void onQuery(const json::Value &v)
-{
-	std::cout << "event:       onQuery\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "message:     " << v.valueOr("message", "").toString() << "\n";
-}
-
-void onTopic(const json::Value &v)
-{
-	std::cout << "event:       onTopic\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "origin:      " << v.valueOr("origin", "").toString() << "\n";
-	std::cout << "channel:     " << v.valueOr("channel", "").toString() << "\n";
-	std::cout << "topic:       " << v.valueOr("topic", "").toString() << "\n";
-}
-
-void onWhois(const json::Value &v)
-{
-	std::cout << "event:       onWhois\n";
-	std::cout << "server:      " << v.valueOr("server", "").toString() << "\n";
-	std::cout << "nickname:    " << v.valueOr("nickname", "").toString() << "\n";
-	std::cout << "username:    " << v.valueOr("username", "").toString() << "\n";
-	std::cout << "host:        " << v.valueOr("host", "").toString() << "\n";
-	std::cout << "realname:    " << v.valueOr("realname", "").toString() << "\n";
-}
-
-const std::unordered_map<std::string, std::function<void (const json::Value &)>> events{
-	{ "onChannelMode",	onChannelMode		},
-	{ "onChannelNotice",	onChannelNotice		},
-	{ "onConnect",		onConnect		},
-	{ "onInvite",		onInvite		},
-	{ "onJoin",		onJoin			},
-	{ "onKick",		onKick			},
-	{ "onMessage",		onMessage		},
-	{ "onMe",		onMe			},
-	{ "onMode",		onMode			},
-	{ "onNames",		onNames			},
-	{ "onNick",		onNick			},
-	{ "onNotice",		onNotice		},
-	{ "onPart",		onPart			},
-	{ "onQuery",		onQuery			},
-	{ "onTopic",		onTopic			},
-	{ "onWhois",		onWhois			}
-};
-
-} // !namespace
-
-Watch::Watch()
-	: RemoteCommand("watch", "General")
-{
-}
-
-std::vector<RemoteCommand::Option> Watch::options() const
-{
-	return {{ "format", "f", "format", "format", "output format" }};
-}
-
-std::string Watch::help() const
-{
-	std::ostringstream oss;
-
-	oss << "Start watching irccd events.\n\n";
-	oss << "You can use different output formats, native which is a human readable\n";
-	oss << "format or json, pretty formatted json.";
-
-	return oss.str();
-}
-
-json::Value Watch::request(Irccdctl &ctl, const RemoteCommandRequest &request) const
-{
-	std::string format = request.optionOr("format", "native");
-
-	if (format != "native" && format != "json")
-		throw std::invalid_argument("invalid format given: " + format);
-
-	while (ctl.connection().isConnected()) {
-		try {
-			auto object = ctl.connection().next(-1);
-			auto it = events.find(object.valueOr("event", "").toString());
-
-			/* Silently ignore to avoid breaking user output */
-			if (it == events.end())
-				continue;
-
-			if (format == "json") {
-				std::cout << object.toJson() << std::endl;
-			} else {
-				it->second(object);
-				std::cout << std::endl;
-			}
-		} catch (...) {
-		}
-	}
-
-	throw std::runtime_error("connection lost");
-
-	return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/command/watch.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * command-watch.h -- implementation of irccdctl watch
- *
- * 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 _IRCCDCTL_COMMAND_WATCH_H_
-#define _IRCCDCTL_COMMAND_WATCH_H_
-
-/**
- * @file command-watch.h
- * @brief Implementation of irccdctl watch.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class Watch
- * @brief Implementation of irccdctl watch.
- */
-class Watch : public RemoteCommand {
-public:
-	Watch();
-
-	std::vector<Option> options() const override;
-
-	/**
-	 * @copydoc RemoteCommand::help
-	 */
-	std::string help() const override;
-
-	/**
-	 * @copydoc RemoteCommand::request
-	 */
-	json::Value request(Irccdctl &ctl, const RemoteCommandRequest &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- a/lib/irccd/config.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/config.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -30,15 +30,13 @@
 #  include <cstdlib>
 #endif
 
-#include <irccd/private/ini.h>
-
-#include <irccd/logger.h>
-#include <irccd/path.h>
-#include <irccd/private/sockets.h>
-#include <irccd/system.h>
-#include <irccd/util.h>
-
 #include "config.h"
+#include "ini.h"
+#include "logger.h"
+#include "path.h"
+#include "sockets.h"
+#include "system.h"
+#include "util.h"
 #include "irccd.h"
 
 using namespace std;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/connection.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,48 @@
+/*
+ * connection.cpp -- value wrapper for connecting to irccd
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd/logger.h>
+
+#include "connection.h"
+
+namespace irccd {
+
+json::Value Connection::next(const std::string &name, int timeout)
+{
+	m_timer.reset();
+
+	while (isConnected()) {
+		json::Value object = next(clamp(timeout));
+
+		if (object.isObject() && object["response"].toString() == name)
+			return object;
+	}
+
+	throw std::runtime_error("connection lost");
+}
+
+void Connection::verify(const std::string &name, int timeout)
+{
+	auto object = next(name, timeout);
+	auto value = object.at("status").toString();
+
+	if (!value.empty() && value != "ok")
+		throw std::runtime_error(object.at("error").toString());
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/connection.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,238 @@
+/*
+ * connection.h -- value wrapper for connecting to irccd
+ *
+ * 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_CONNECTION_H_
+#define _IRCCD_CONNECTION_H_
+
+#include <cassert>
+#include <stdexcept>
+
+#include <irccd/json.h>
+#include <irccd/system.h>
+#include <irccd/util.h>
+
+#include "elapsed-timer.h"
+#include "sockets.h"
+
+namespace irccd {
+
+/**
+ * @class Connection
+ * @brief Abstract class for connecting to irccd from Ip or Local addresses.
+ */
+class Connection {
+protected:
+	ElapsedTimer m_timer;
+
+	/**
+	 * Clamp the time to wait to be sure that it will be never less than 0.
+	 */
+	inline int clamp(int timeout) noexcept
+	{
+		return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed());
+	}
+
+public:
+	/**
+	 * Default constructor.
+	 */
+	Connection() = default;
+
+	/**
+	 * Default destructor.
+	 */
+	virtual ~Connection() = default;
+
+	/**
+	 * Wait for the next requested response.
+	 *
+	 * @param name the response name
+	 * @param timeout the optional timeout
+	 * @return the object
+	 * @throw net::Error on errors or on timeout
+	 */
+	json::Value next(const std::string &name, int timeout = 30000);
+
+	/**
+	 * Just wait if the operation succeeded.
+	 *
+	 * @param name the response name
+	 * @param timeout the timeout
+	 */
+	void verify(const std::string &name, int timeout = 30000);
+
+	/**
+	 * Check if the socket is still connected.
+	 *
+	 * @return true if connected
+	 */
+	virtual bool isConnected() const noexcept = 0;
+
+	/**
+	 * Try to connect to the host.
+	 *
+	 * @param timeout the maximum time in milliseconds
+	 * @throw net::Error on errors or timeout
+	 */
+	virtual void connect(int timeout = 30000) = 0;
+
+	/**
+	 * Try to send the message in 30 seconds. The message must not end with \r\n\r\n, it is added automatically.
+	 *
+	 * @pre msg must not be empty
+	 * @param msg the message to send
+	 * @param timeout the maximum time in milliseconds
+	 * @throw net::Error on errors
+	 */
+	virtual void send(std::string msg, int timeout = 30000) = 0;
+
+	/**
+	 * Get the next event from irccd.
+	 *
+	 * This functions throws if the connection is lost.
+	 *
+	 * @param timeout the maximum time in milliseconds
+	 * @return the next event
+	 * @throw net::Error on errors or disconnection
+	 */
+	virtual json::Value next(int timeout = 30000) = 0;
+};
+
+/**
+ * @class ConnectionBase
+ * @brief Implementation for Ip or Local.
+ */
+template <typename Address>
+class ConnectionBase : public Connection {
+private:
+	net::SocketTcp<Address> m_socket;
+	net::Listener<> m_listener;
+	Address m_address;
+
+	/* Input buffer */
+	std::string m_input;
+
+public:
+	/**
+	 * Construct the socket but do not connect immediately.
+	 *
+	 * @param address the address
+	 */
+	ConnectionBase(Address address)
+		: m_address(std::move(address))
+	{
+		m_socket.set(net::option::SockBlockMode{false});
+		m_listener.set(m_socket.handle(), net::Condition::Readable);
+	}
+
+	/**
+	 * @copydoc Connection::isConnected
+	 */
+	bool isConnected() const noexcept override
+	{
+		return m_socket.state() == net::State::Connected;
+	}
+
+	/**
+	 * @copydoc Connection::connect
+	 */
+	void connect(int timeout) override;
+
+	/**
+	 * @copydoc Connection::send
+	 */
+	void send(std::string msg, int timeout) override;
+
+	/**
+	 * @copydoc Connection::next
+	 */
+	json::Value next(int timeout) override;
+};
+
+template <typename Address>
+void ConnectionBase<Address>::connect(int timeout)
+{
+	m_socket.connect(m_address);
+
+	if (m_socket.state() == net::State::Connecting) {
+		m_listener.set(m_socket.handle(), net::Condition::Writable);
+		m_listener.wait(timeout);
+		m_socket.connect();
+		m_listener.unset(m_socket.handle(), net::Condition::Writable);
+	}
+}
+
+template <typename Address>
+void ConnectionBase<Address>::send(std::string msg, int timeout)
+{
+	assert(!msg.empty());
+
+	/* Add termination */
+	msg += "\r\n\r\n";
+
+	m_listener.remove(m_socket.handle());
+	m_listener.set(m_socket.handle(), net::Condition::Writable);
+	m_timer.reset();
+
+	while (!msg.empty()) {
+		/* Do not wait the time that is already passed */
+		m_listener.wait(clamp(timeout));
+
+		/* Try to send at most as possible */
+		msg.erase(0, m_socket.send(msg));
+	}
+
+	/* Timeout? */
+	if (!msg.empty())
+		throw std::runtime_error("operation timed out while sending to irccd");
+}
+
+template <typename Address>
+json::Value ConnectionBase<Address>::next(int timeout)
+{
+	/* Maybe there is already something */
+	std::string buffer = util::nextNetwork(m_input);
+
+	m_listener.remove(m_socket.handle());
+	m_listener.set(m_socket.handle(), net::Condition::Readable);
+	m_timer.reset();
+
+	/* Read if there is nothing */
+	while (buffer.empty() && isConnected()) {
+		/* Wait and read */
+		m_listener.wait(clamp(timeout));
+		m_input += m_socket.recv(512);
+
+		/* Finally try */
+		buffer = util::nextNetwork(m_input);
+	}
+
+	if (!isConnected())
+		throw std::runtime_error("connection lost");
+
+	json::Value value(json::Buffer{buffer});
+
+	if (!value.isObject())
+		throw std::invalid_argument("invalid message received");
+
+	return value;
+}
+
+} // !irccd
+
+#endif // !_IRCCD_CONNECTION_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/directory.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,188 @@
+/*
+ * directory.cpp -- open and read directories
+ *
+ * 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 <sstream>
+#include <stdexcept>
+
+#include "directory.h"
+
+#if defined(_WIN32)
+#  include <Windows.h>
+#else
+#  include <cstring>
+#  include <cerrno>
+
+#  include <sys/types.h>
+#  include <dirent.h>
+#endif
+
+namespace irccd {
+
+#if defined(_WIN32)
+
+namespace {
+
+std::string systemError()
+{
+	LPSTR error = nullptr;
+	std::string errmsg = "Unknown error";
+
+	FormatMessageA(
+		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+		NULL,
+		GetLastError(),
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPSTR)&error, 0, NULL);
+
+	if (error) {
+		errmsg = std::string(error);
+		LocalFree(error);
+	}
+
+	return errmsg;
+}
+
+} // !namespace
+
+void Directory::systemLoad(const std::string &path, int flags)
+{
+	std::ostringstream oss;
+	HANDLE handle;
+	WIN32_FIND_DATA fdata;
+
+	oss << path << "\\*";
+	handle = FindFirstFile(oss.str().c_str(), &fdata);
+
+	if (handle == nullptr)
+		throw std::runtime_error(systemError());
+
+	do {
+		DirectoryEntry entry;
+
+		entry.name = fdata.cFileName;
+		if (entry.name == "." && !(flags & Directory::Dot))
+			continue;
+		if (entry.name == ".." && !(flags & Directory::DotDot))
+			continue;
+
+		switch (fdata.dwFileAttributes) {
+		case FILE_ATTRIBUTE_DIRECTORY:
+			entry.type = DirectoryEntry::Dir;
+			break;
+		case FILE_ATTRIBUTE_NORMAL:
+			entry.type = DirectoryEntry::File;
+			break;
+		case FILE_ATTRIBUTE_REPARSE_POINT:
+			entry.type = DirectoryEntry::Link;
+			break;
+		default:
+			break;
+		}
+
+		m_list.push_back(entry);
+	} while (FindNextFile(handle, &fdata) != 0);
+
+	FindClose(handle);
+}
+
+#else
+
+void Directory::systemLoad(const std::string &path, int flags)
+{
+	DIR *dp;
+	struct dirent *ent;
+
+	if ((dp = opendir(path.c_str())) == nullptr)
+		throw std::runtime_error(std::strerror(errno));
+
+	while ((ent = readdir(dp)) != nullptr) {
+		DirectoryEntry entry;
+
+		entry.name = ent->d_name;
+		if (entry.name == "." && !(flags & Directory::Dot))
+			continue;
+		if (entry.name == ".." && !(flags & Directory::DotDot))
+			continue;
+
+		switch (ent->d_type) {
+		case DT_DIR:
+			entry.type = DirectoryEntry::Dir;
+			break;
+		case DT_REG:
+			entry.type = DirectoryEntry::File;
+			break;
+		case DT_LNK:
+			entry.type = DirectoryEntry::Link;
+			break;
+		default:
+			break;
+		}
+
+		m_list.push_back(entry);
+	}
+
+	closedir(dp);
+}
+
+#endif
+
+bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2)
+{
+	return e1.name == e2.name && e1.type == e2.type;
+}
+
+Directory::Directory()
+{
+}
+
+Directory::Directory(const std::string &path, int flags)
+{
+	systemLoad(path, flags);
+}
+
+Directory::List::iterator Directory::begin()
+{
+	return m_list.begin();
+}
+
+Directory::List::const_iterator Directory::cbegin() const
+{
+	return m_list.cbegin();
+}
+
+Directory::List::iterator Directory::end()
+{
+	return m_list.end();
+}
+
+Directory::List::const_iterator Directory::cend() const
+{
+	return m_list.cend();
+}
+
+int Directory::count() const
+{
+	return m_list.size();
+}
+
+bool operator==(const Directory &d1, const Directory &d2)
+{
+	return d1.m_list == d2.m_list;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/directory.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,139 @@
+/*
+ * directory.h -- open and read directories
+ *
+ * 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 _DIRECTORY_H_
+#define _DIRECTORY_H_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * @class Entry
+ * @brief entry in the directory list
+ */
+class DirectoryEntry {
+public:
+	/**
+	 * @enum Type
+	 * @brief Describe the type of an entry
+	 */
+	enum Type {
+		Unknown		= 0,
+		File,
+		Dir,
+		Link
+	};
+
+	std::string	name;		//! name of entry (base name)
+	Type		type{Unknown};	//! type of file
+
+	friend bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2);
+};
+
+/**
+ * @class Directory
+ * @brief class to manipulate directories
+ *
+ * This class allow the user to iterate directories in a for range based
+ * loop using iterators.
+ */
+class Directory {
+public:
+	/**
+	 * @enum Flags
+	 * @brief optional flags to read directories
+	 */
+	enum Flags {
+		Dot	= (1 << 0),	//!< If set, lists "." too
+		DotDot	= (1 << 1)	//!< If set, lists ".." too
+	};
+
+	using List = std::vector<DirectoryEntry>;
+
+	// C++ Container compatibility
+	using value_type	= List::value_type;
+	using iterator		= List::iterator;
+	using const_iterator	= List::const_iterator;
+
+private:
+	List m_list;
+
+	void systemLoad(const std::string &path, int flags);
+
+public:
+	/**
+	 * Default constructor, does nothing.
+	 */
+	Directory();
+
+	/**
+	 * Open a directory and read all its content.
+	 * @param path the path
+	 * @param flags the optional flags
+	 */
+	Directory(const std::string &path, int flags = 0);
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~Directory() = default;
+
+	/**
+	 * Return an iterator the beginning.
+	 *
+	 * @return the iterator
+	 */
+	List::iterator begin();
+
+	/**
+	 * Return a const iterator the beginning.
+	 *
+	 * @return the iterator
+	 */
+	List::const_iterator cbegin() const;
+
+	/**
+	 * Return an iterator to past the end.
+	 *
+	 * @return the iterator
+	 */
+	List::iterator end();
+
+	/**
+	 * Return a const iterator to past the end.
+	 *
+	 * @return the iterator
+	 */
+	List::const_iterator cend() const;
+
+	/**
+	 * Get the number of entries in the directory.
+	 *
+	 * @return the number
+	 */
+	int count() const;
+
+	friend bool operator==(const Directory &d1, const Directory &d2);
+};
+
+} // !irccd
+
+#endif // !_DIRECTORY_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/elapsed-timer.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * elapsed-timer.cpp -- measure elapsed time
+ *
+ * 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 "elapsed-timer.h"
+
+using std::chrono::duration_cast;
+using std::chrono::high_resolution_clock;
+using std::chrono::milliseconds;
+
+namespace irccd {
+
+ElapsedTimer::ElapsedTimer() noexcept
+{
+	m_last = high_resolution_clock::now();
+}
+
+void ElapsedTimer::pause() noexcept
+{
+	/*
+	 * When we put the timer on pause, do not forget to set the already
+	 * elapsed time.
+	 */
+	(void)elapsed();
+	m_paused = true;
+}
+
+void ElapsedTimer::restart() noexcept
+{
+	m_paused = false;
+	m_last = high_resolution_clock::now();
+}
+
+void ElapsedTimer::reset() noexcept
+{
+	m_elapsed = 0;
+	m_last = high_resolution_clock::now();
+}
+
+unsigned ElapsedTimer::elapsed() noexcept
+{
+	if (!m_paused) {
+		m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count();
+		m_last = high_resolution_clock::now();
+	}
+
+	return m_elapsed;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/elapsed-timer.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * elapsed-timer.h -- measure elapsed time
+ *
+ * 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_ELAPSED_TIMER_H_
+#define _IRCCD_ELAPSED_TIMER_H_
+
+/**
+ * @file ElapsedTimer.h
+ * @brief Measure elapsed time
+ */
+
+#include <chrono>
+
+namespace irccd {
+
+/**
+ * @class ElapsedTimer
+ * @brief Measure elapsed time
+ *
+ * This class provides an abstraction to measure elapsed time since the
+ * construction of the object.
+ *
+ * It uses std::chrono::high_resolution_clock for more precision and uses
+ * milliseconds only.
+ */
+class ElapsedTimer {
+public:
+	using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
+
+private:
+	TimePoint m_last;
+	bool m_paused{false};
+	unsigned m_elapsed{0};
+
+public:
+	/**
+	 * Construct the elapsed timer, start counting.
+	 */
+	ElapsedTimer() noexcept;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~ElapsedTimer() = default;
+
+	/**
+	 * Put the timer on pause, the already elapsed time is stored.
+	 */
+	void pause() noexcept;
+
+	/**
+	 * Restart the timer, does not reset it.
+	 */
+	void restart() noexcept;
+
+	/**
+	 * Reset the timer to 0.
+	 */
+	void reset() noexcept;
+
+	/**
+	 * Get the number of elapsed milliseconds.
+	 *
+	 * @return the milliseconds
+	 */
+	unsigned elapsed() noexcept;
+};
+
+} // !irccd
+
+#endif // !_IRCCD_ELAPSED_TIMER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/filesystem.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,187 @@
+/*
+ * filesystem.cpp -- some file system operation
+ *
+ * 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 <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+#include <sstream>
+
+#include <irccd-config.h>
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+#  include <direct.h>
+#  include <shlwapi.h>
+#else
+#  include <sys/stat.h>
+#  include <climits>
+#  include <unistd.h>
+#  include <libgen.h>
+#endif
+
+#include "filesystem.h"
+
+namespace irccd {
+
+namespace fs {
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+const char Separator('\\');
+#else
+const char Separator('/');
+#endif
+
+std::string baseName(std::string path)
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	std::size_t pos;
+
+	pos = path.find_last_of('\\');
+	if (pos == std::string::npos)
+		pos = path.find_last_of('/');
+	if (pos == std::string::npos)
+		return path;
+
+	return path.substr(pos + 1);
+#else
+	return basename(&path[0]);
+#endif
+}
+
+std::string dirName(std::string path)
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	std::size_t pos;
+
+	pos = path.find_last_of('\\');
+	if (pos == std::string::npos)
+		pos = path.find_last_of('/');
+	if (pos == std::string::npos)
+		return path;
+
+	return path.substr(0, pos);
+#else
+	return dirname(&path[0]);
+#endif
+}
+
+bool isAbsolute(const std::string &path) noexcept
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	return !isRelative(path);
+#else
+	return path.size() > 0 && path[0] == '/';
+#endif
+}
+
+bool isRelative(const std::string &path) noexcept
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	return PathIsRelativeA(path.c_str());
+#else
+	return !isAbsolute(path);
+#endif
+}
+
+bool exists(const std::string &path)
+{
+#if defined(HAVE_ACCESS)
+	return access(path.c_str(), F_OK) == 0;
+#elif defined(HAVE_STAT)
+	struct stat st;
+
+	return (stat(path.c_str(), &st) == 0);
+#else
+	// worse fallback
+	std::FILE *file = std::fopen(path.c_str(), "r");
+	bool result;
+
+	if (file != nullptr) {
+		result = true;
+		std::fclose(file);
+	} else {
+		result = false;
+	}
+
+	return result;
+#endif
+}
+
+void mkdir(const std::string &dir, int mode)
+{
+	std::ostringstream oss;
+
+	oss << "mkdir: ";
+
+	for (std::size_t i = 0; i < dir.length(); ++i) {
+		if (dir[i] != '/' && dir[i] != '\\')
+			continue;
+
+		std::string part = dir.substr(0, i);
+		if (part.length() <= 0 || exists(part))
+			continue;
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+		if (::_mkdir(part.c_str()) < 0) {
+#else
+		if (::mkdir(part.c_str(), mode) < 0) {
+#endif
+			oss << part << ": " << std::strerror(errno);
+			throw std::runtime_error(oss.str());
+		}
+	}
+
+	// Last part
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	if (::_mkdir(dir.c_str()) < 0) {
+#else
+	if (::mkdir(dir.c_str(), mode) < 0) {
+#endif
+		oss << dir << ": " << std::strerror(errno);
+		throw std::runtime_error(oss.str());
+	}
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	// Windows's mkdir does not use mode.
+	(void)mode;
+#endif
+}
+
+std::string cwd()
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	char path[MAX_PATH];
+
+	if (!GetCurrentDirectoryA(sizeof (path), path))
+		throw std::runtime_error("failed to get current working directory");
+
+	return path;
+#else
+	char path[PATH_MAX];
+
+	if (getcwd(path, sizeof (path)) == nullptr)
+		throw std::runtime_error(std::strerror(errno));
+
+	return path;
+#endif
+}
+
+} // !fs
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/filesystem.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,42 @@
+/*
+ * filesystem.h -- some file system operation
+ *
+ * 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_FILESYSTEM_H_
+#define _IRCCD_FILESYSTEM_H_
+
+#include <string>
+
+namespace irccd {
+
+namespace fs {
+
+extern const char Separator;
+
+std::string baseName(std::string path);
+std::string dirName(std::string path);
+bool isAbsolute(const std::string &path) noexcept;
+bool isRelative(const std::string &path) noexcept;
+bool exists(const std::string &path);
+void mkdir(const std::string &dir, int mode = 0700);
+std::string cwd();
+
+} // !fs
+
+} // !irccd
+
+#endif // !_IRCCD_FILESYSTEM_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/ini.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,427 @@
+/*
+ * ini.cpp -- .ini file parsing
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cassert>
+#include <cctype>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+#if defined(_WIN32)
+#  include <Shlwapi.h>	// for PathIsRelative
+#endif
+
+#include "ini.h"
+
+namespace {
+
+using namespace irccd;
+using namespace irccd::ini;
+
+using StreamIterator = std::istreambuf_iterator<char>;
+using TokenIterator = std::vector<Token>::const_iterator;
+
+inline bool isAbsolute(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return !PathIsRelative(path.c_str());
+#else
+	return path.size() > 0 && path[0] == '/';
+#endif
+}
+
+inline bool isQuote(char c) noexcept
+{
+	return c == '\'' || c == '"';
+}
+
+inline bool isSpace(char c) noexcept
+{
+	/* Custom version because std::isspace includes \n as space */
+	return c == ' ' || c == '\t';
+}
+
+inline bool isList(char c) noexcept
+{
+	return c == '(' || c == ')' || c == ',';
+}
+
+inline bool isReserved(char c) noexcept
+{
+	return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '=';
+}
+
+void analyzeLine(int &line, int &column, StreamIterator &it) noexcept
+{
+	assert(*it == '\n');
+
+	++ line;
+	++ it;
+	column = 0;
+}
+
+void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept
+{
+	assert(*it == '#');
+
+	while (it != end && *it != '\n') {
+		++ column;
+		++ it;
+	}
+}
+
+void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
+{
+	assert(isSpace(*it));
+
+	while (it != end && isSpace(*it)) {
+		++ column;
+		++ it;
+	}
+}
+
+void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept
+{
+	assert(isList(*it));
+
+	switch (*it++) {
+	case '(':
+		list.emplace_back(Token::ListBegin, line, column++);
+		break;
+	case ')':
+		list.emplace_back(Token::ListEnd, line, column++);
+		break;
+	case ',':
+		list.emplace_back(Token::Comma, line, column++);
+		break;
+	default:
+		break;
+	}
+}
+
+void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
+{
+	assert(*it == '[');
+
+	std::string value;
+	int save = column;
+
+	/* Read section name */
+	++ it;
+	while (it != end && *it != ']') {
+		if (*it == '\n')
+			throw Error(line, column, "section not terminated, missing ']'");
+		if (isReserved(*it))
+			throw Error(line, column, "section name expected after '[', got '" + std::string(1, *it) + "'");
+		++ column;
+		value += *it++;
+	}
+
+	if (it == end)
+		throw Error(line, column, "section name expected after '[', got <EOF>");
+
+	/* Remove ']' */
+	++ it;
+
+	list.emplace_back(Token::Section, line, save, std::move(value));
+}
+
+void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it)
+{
+	assert(*it == '=');
+
+	list.push_back({ Token::Assign, line, column++ });
+	++ it;
+}
+
+void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
+{
+	std::string value;
+	int save = column;
+	char quote = *it++;
+
+	while (it != end && *it != quote) {
+		// TODO: escape sequence
+		++ column;
+		value += *it++;
+	}
+
+	if (it == end)
+		throw Error(line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>");
+
+	/* Remove quote */
+	++ it;
+
+	list.push_back({ Token::QuotedWord, line, save, std::move(value) });
+}
+
+void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
+{
+	assert(!isReserved(*it));
+
+	std::string value;
+	int save = column;
+
+	while (it != end && !std::isspace(*it) && !isReserved(*it)) {
+		++ column;
+		value += *it++;
+	}
+
+	list.push_back({ Token::Word, line, save, std::move(value) });
+}
+
+void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
+{
+	assert(*it == '@');
+
+	std::string include;
+	int save = column;
+
+	/* Read include */
+	++ it;
+	while (it != end && !isSpace(*it)) {
+		++ column;
+		include += *it++;
+	}
+
+	if (include != "include")
+		throw Error(line, column, "expected include after '@' token");
+
+	list.push_back({ Token::Include, line, save });
+}
+
+Tokens analyze(StreamIterator &it, StreamIterator end)
+{
+	Tokens list;
+	int line = 1;
+	int column = 0;
+
+	while (it != end) {
+		if (*it == '\n')
+			analyzeLine(line, column, it);
+		else if (*it == '#')
+			analyzeComment(column, it, end);
+		else if (*it == '[')
+			analyzeSection(list, line, column, it, end);
+		else if (*it == '=')
+			analyzeAssign(list, line, column, it);
+		else if (isSpace(*it))
+			analyzeSpaces(column, it, end);
+		else if (*it == '@')
+			analyzeInclude(list, line, column, it, end);
+		else if (isQuote(*it))
+			analyzeQuotedWord(list, line, column, it, end);
+		else if (isList(*it))
+			analyzeList(list, line, column, it);
+		else
+			analyzeWord(list, line, column, it, end);
+	}
+
+	return list;
+}
+
+void parseOptionValueSimple(Option &option, TokenIterator &it)
+{
+	assert(it->type() == Token::Word || it->type() == Token::QuotedWord);
+
+	option.push_back((it++)->value());
+}
+
+void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end)
+{
+	assert(it->type() == Token::ListBegin);
+
+	TokenIterator save = it++;
+
+	while (it != end && it->type() != Token::ListEnd) {
+		switch (it->type()) {
+		case Token::Comma:
+			/* Previous must be a word */
+			if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord)
+				throw Error(it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'");
+
+			++ it;
+			break;
+		case Token::Word:
+		case Token::QuotedWord:
+			option.push_back((it++)->value());
+			break;
+		default:
+			throw Error(it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct");
+			break;
+		}
+	}
+
+	if (it == end)
+		throw Error(save->line(), save->column(), "unterminated list construct");
+
+	/* Remove ) */
+	++ it;
+}
+
+void parseOption(Section &sc, TokenIterator &it, TokenIterator end)
+{
+	Option option(it->value());
+
+	TokenIterator save = it;
+
+	/* No '=' or something else? */
+	if (++it == end)
+		throw Error(save->line(), save->column(), "expected '=' assignment, got <EOF>");
+	if (it->type() != Token::Assign)
+		throw Error(it->line(), it->column(), "expected '=' assignment, got " + it->value());
+
+	/* Empty options are allowed so just test for words */
+	if (++it != end) {
+		if (it->type() == Token::Word || it->type() == Token::QuotedWord)
+			parseOptionValueSimple(option, it);
+		else if (it->type() == Token::ListBegin)
+			parseOptionValueList(option, it, end);
+	}
+
+	sc.push_back(std::move(option));
+}
+
+void parseInclude(Document &doc, TokenIterator &it, TokenIterator end)
+{
+	TokenIterator save = it;
+
+	if (++it == end)
+		throw Error(save->line(), save->column(), "expected file name after '@include' statement, got <EOF>");
+	if (it->type() != Token::Word && it->type() != Token::QuotedWord)
+		throw Error(it->line(), it->column(), "expected file name after '@include' statement, got " + it->value());
+	if (doc.path().empty())
+		throw Error(it->line(), it->column(), "'@include' statement invalid with buffer documents");
+
+	std::string value = (it++)->value();
+	std::string file;
+
+	if (!isAbsolute(value)) {
+#if defined(_WIN32)
+		file = doc.path() + "\\" + value;
+#else
+		file = doc.path() + "/" + value;
+#endif
+	} else {
+		file = value;
+	}
+
+	Document child(File{file});
+
+	for (const auto &sc : child)
+		doc.push_back(sc);
+}
+
+void parseSection(Document &doc, TokenIterator &it, TokenIterator end)
+{
+	Section sc(it->value());
+
+	/* Skip [section] */
+	++ it;
+
+	/* Read until next section */
+	while (it != end && it->type() != Token::Section) {
+		if (it->type() != Token::Word)
+			throw Error(it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition");
+
+		parseOption(sc, it, end);
+	}
+
+	doc.push_back(std::move(sc));
+}
+
+void parse(Document &doc, const Tokens &tokens)
+{
+	TokenIterator it = tokens.cbegin();
+	TokenIterator end = tokens.cend();
+
+	while (it != end) {
+		/* Just ignore this */
+		switch (it->type()) {
+		case Token::Include:
+			parseInclude(doc, it, end);
+			break;
+		case Token::Section:
+			parseSection(doc, it, end);
+			break;
+		default:
+			throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document");
+		}
+	}
+}
+
+} // !namespace
+
+namespace irccd {
+
+namespace ini {
+
+Tokens Document::analyze(const File &file)
+{
+	std::fstream stream(file.path);
+
+	if (!stream)
+		throw std::runtime_error(std::strerror(errno));
+
+	std::istreambuf_iterator<char> it(stream);
+	std::istreambuf_iterator<char> end;
+
+	return ::analyze(it, end);
+}
+
+Tokens Document::analyze(const Buffer &buffer)
+{
+	std::istringstream stream(buffer.text);
+	std::istreambuf_iterator<char> it(stream);
+	std::istreambuf_iterator<char> end;
+
+	return ::analyze(it, end);
+}
+
+Document::Document(const File &file)
+	: m_path(file.path)
+{
+	/* Update path */
+	auto pos = m_path.find_last_of("/\\");
+
+	if (pos != std::string::npos)
+		m_path.erase(pos);
+	else
+		m_path = ".";
+
+	parse(*this, analyze(file));
+}
+
+Document::Document(const Buffer &buffer)
+{
+	parse(*this, analyze(buffer));
+}
+
+void Document::dump(const Tokens &tokens)
+{
+	for (const Token &token: tokens)
+		// TODO: add better description
+		std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl;
+}
+
+} // !ini
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/ini.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,538 @@
+/*
+ * ini.h -- .ini file parsing
+ *
+ * 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 _INI_H_
+#define _INI_H_
+
+/**
+ * @file Ini.h
+ * @brief Configuration file parser.
+ */
+
+#include <algorithm>
+#include <exception>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * Namespace for ini related classes.
+ */
+namespace ini {
+
+class Document;
+
+/**
+ * @class Error
+ * @brief Error in a file
+ */
+class Error : public std::exception {
+private:
+	int m_line;		//!< line number
+	int m_column;		//!< line column
+	std::string m_message;	//!< error message
+
+public:
+	/**
+	 * Constructor.
+	 *
+	 * @param l the line
+	 * @param c the column
+	 * @param m the message
+	 */
+	inline Error(int l, int c, std::string m) noexcept
+		: m_line(l)
+		, m_column(c)
+		, m_message(std::move(m))
+	{
+	}
+
+	/**
+	 * Get the line number.
+	 *
+	 * @return the line
+	 */
+	inline int line() const noexcept
+	{
+		return m_line;
+	}
+
+	/**
+	 * Get the column number.
+	 *
+	 * @return the column
+	 */
+	inline int column() const noexcept
+	{
+		return m_column;
+	}
+
+	/**
+	 * Return the raw error message (no line and column shown).
+	 *
+	 * @return the error message
+	 */
+	const char *what() const noexcept override
+	{
+		return m_message.c_str();
+	}
+};
+
+/**
+ * @class Token
+ * @brief Describe a token read in the .ini source
+ *
+ * This class can be used when you want to parse a .ini file yourself.
+ *
+ * @see Document::analyze
+ */
+class Token {
+public:
+	/**
+	 * @brief Token type
+	 */
+	enum Type {
+		Include,	//!< include statement
+		Section,	//!< [section]
+		Word,		//!< word without quotes
+		QuotedWord,	//!< word with quotes
+		Assign,		//!< = assignment
+		ListBegin,	//!< begin of list (
+		ListEnd,	//!< end of list )
+		Comma		//!< list separation
+	};
+
+private:
+	Type m_type;
+	int m_line;
+	int m_column;
+	std::string m_value;
+
+public:
+	/**
+	 * Construct a token.
+	 *
+	 * @param type the type
+	 * @param line the line
+	 * @param column the column
+	 * @param value the value
+	 */
+	Token(Type type, int line, int column, std::string value = "") noexcept
+		: m_type(type)
+		, m_line(line)
+		, m_column(column)
+	{
+		switch (type) {
+		case Include:
+			m_value = "@include";
+			break;
+		case Section:
+		case Word:
+		case QuotedWord:
+			m_value = value;
+			break;
+		case Assign:
+			m_value = "=";
+			break;
+		case ListBegin:
+			m_value = "(";
+			break;
+		case ListEnd:
+			m_value = ")";
+			break;
+		case Comma:
+			m_value = ",";
+			break;
+		default:
+			break;
+		}
+	}
+
+	/**
+	 * Get the type.
+	 *
+	 * @return the type
+	 */
+	inline Type type() const noexcept
+	{
+		return m_type;
+	}
+
+	/**
+	 * Get the line.
+	 *
+	 * @return the line
+	 */
+	inline int line() const noexcept
+	{
+		return m_line;
+	}
+
+	/**
+	 * Get the column.
+	 *
+	 * @return the column
+	 */
+	inline int column() const noexcept
+	{
+		return m_column;
+	}
+
+	/**
+	 * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the
+	 * characters parsed.
+	 *
+	 * @return the value
+	 */
+	inline const std::string &value() const noexcept
+	{
+		return m_value;
+	}
+};
+
+/**
+ * List of tokens in order they are analyzed.
+ */
+using Tokens = std::vector<Token>;
+
+/**
+ * @class Option
+ * @brief Option definition.
+ */
+class Option : public std::vector<std::string> {
+private:
+	std::string m_key;
+
+public:
+	/**
+	 * Construct an empty option.
+	 *
+	 * @param key the key
+	 * @param value the value
+	 */
+	inline Option(std::string key) noexcept
+		: std::vector<std::string>()
+		, m_key(std::move(key))
+	{
+	}
+
+	/**
+	 * Construct a single option.
+	 *
+	 * @param key the key
+	 * @param value the value
+	 */
+	inline Option(std::string key, std::string value) noexcept
+		: m_key(std::move(key))
+	{
+		push_back(std::move(value));
+	}
+
+	/**
+	 * Construct a list option.
+	 *
+	 * @param key the key
+	 * @param values the values
+	 */
+	inline Option(std::string key, std::vector<std::string> values) noexcept
+		: std::vector<std::string>(std::move(values))
+		, m_key(std::move(key))
+	{
+	}
+
+	/**
+	 * Get the option key.
+	 *
+	 * @return the key
+	 */
+	inline const std::string &key() const noexcept
+	{
+		return m_key;
+	}
+
+	/**
+	 * Get the option value.
+	 *
+	 * @return the value
+	 */
+	inline const std::string &value() const noexcept
+	{
+		static std::string dummy;
+
+		return empty() ? dummy : (*this)[0];
+	}
+};
+
+/**
+ * @class Section
+ * @brief Section that contains one or more options.
+ */
+class Section : public std::vector<Option> {
+private:
+	std::string m_key;
+
+public:
+	/**
+	 * Construct a section with its name.
+	 *
+	 * @param key the key
+	 */
+	inline Section(std::string key) noexcept
+		: m_key(std::move(key))
+	{
+	}
+
+	/**
+	 * Get the section key.
+	 *
+	 * @return the key
+	 */
+	inline const std::string &key() const noexcept
+	{
+		return m_key;
+	}
+
+	/**
+	 * Check if the section contains a specific option.
+	 *
+	 * @param key the option key
+	 * @return true if the option exists
+	 */
+	inline bool contains(const std::string &key) const noexcept
+	{
+		return find(key) != end();
+	}
+
+	/**
+	 * Access an option at the specified key.
+	 *
+	 * @param key the key
+	 * @return the option
+	 * @throw std::out_of_range if the key does not exist
+	 */
+	inline Option &operator[](const std::string &key)
+	{
+		return *find(key);
+	}
+
+	/**
+	 * Access an option at the specified key.
+	 *
+	 * @param key the key
+	 * @return the option
+	 * @throw std::out_of_range if the key does not exist
+	 */
+	inline const Option &operator[](const std::string &key) const
+	{
+		return *find(key);
+	}
+
+	/**
+	 * Find an option by key and return an iterator.
+	 *
+	 * @param key the key
+	 * @return the iterator or end() if not found
+	 */
+	inline iterator find(const std::string &key) noexcept
+	{
+		return std::find_if(begin(), end(), [&] (const auto &o) {
+			return o.key() == key;
+		});
+	}
+
+	/**
+	 * Find an option by key and return an iterator.
+	 *
+	 * @param key the key
+	 * @return the iterator or end() if not found
+	 */
+	inline const_iterator find(const std::string &key) const noexcept
+	{
+		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
+			return o.key() == key;
+		});
+	}
+
+	/**
+	 * Inherited operators.
+	 */
+	using std::vector<Option>::operator[];
+};
+
+/**
+ * @class File
+ * @brief Source for reading .ini files.
+ */
+class File {
+public:
+	/**
+	 * Path to the file.
+	 */
+	std::string path;
+};
+
+/**
+ * @class Buffer
+ * @brief Source for reading ini from text.
+ * @note the include statement is not supported with buffers.
+ */
+class Buffer {
+public:
+	/**
+	 * The ini content.
+	 */
+	std::string text;
+};
+
+/**
+ * @class Document
+ * @brief Ini config file loader
+ */
+class Document : public std::vector<Section> {
+private:
+	std::string m_path;
+
+public:
+	/**
+	 * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid,
+	 * it just means that there are no syntax error.
+	 *
+	 * For example, this class does not allow adding options under no sections and this function will not
+	 * detect that issue.
+	 *
+	 * @param file the file to read
+	 * @return the list of tokens
+	 * @throws Error on errors
+	 */
+	static Tokens analyze(const File &file);
+
+	/**
+	 * Overloaded function for buffers.
+	 *
+	 * @param buffer the buffer to read
+	 * @return the list of tokens
+	 * @throws Error on errors
+	 */
+	static Tokens analyze(const Buffer &buffer);
+
+	/**
+	 * Show all tokens and their description.
+	 *
+	 * @param tokens the tokens
+	 */
+	static void dump(const Tokens &tokens);
+
+	/**
+	 * Construct a document from a file.
+	 *
+	 * @param file the file to read
+	 * @throws Error on errors
+	 */
+	Document(const File &file);
+
+	/**
+	 * Overloaded constructor for buffers.
+	 *
+	 * @param buffer the buffer to read
+	 * @throws Error on errors
+	 */
+	Document(const Buffer &buffer);
+
+	/**
+	 * Get the current document path, only useful when constructed from File source.
+	 *
+	 * @return the path
+	 */
+	inline const std::string &path() const noexcept
+	{
+		return m_path;
+	}
+
+	/**
+	 * Check if a document has a specific section.
+	 *
+	 * @param key the key
+	 */
+	inline bool contains(const std::string &key) const noexcept
+	{
+		return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end();
+	}
+
+	/**
+	 * Access a section at the specified key.
+	 *
+	 * @param key the key
+	 * @return the section
+	 * @throw std::out_of_range if the key does not exist
+	 */
+	inline Section &operator[](const std::string &key)
+	{
+		return *find(key);
+	}
+
+	/**
+	 * Access a section at the specified key.
+	 *
+	 * @param key the key
+	 * @return the section
+	 * @throw std::out_of_range if the key does not exist
+	 */
+	inline const Section &operator[](const std::string &key) const
+	{
+		return *find(key);
+	}
+
+	/**
+	 * Find a section by key and return an iterator.
+	 *
+	 * @param key the key
+	 * @return the iterator or end() if not found
+	 */
+	inline iterator find(const std::string &key) noexcept
+	{
+		return std::find_if(begin(), end(), [&] (const auto &o) {
+			return o.key() == key;
+		});
+	}
+
+	/**
+	 * Find a section by key and return an iterator.
+	 *
+	 * @param key the key
+	 * @return the iterator or end() if not found
+	 */
+	inline const_iterator find(const std::string &key) const noexcept
+	{
+		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
+			return o.key() == key;
+		});
+	}
+
+	/**
+	 * Inherited operators.
+	 */
+	using std::vector<Section>::operator[];
+};
+
+} // !ini
+
+} // !irccd
+
+#endif // !_INI_H_
--- a/lib/irccd/irccd.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/irccd.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -19,9 +19,8 @@
 #include <algorithm>
 #include <stdexcept>
 
-#include "private/filesystem.h"
-
 #include "irccd.h"
+#include "filesystem.h"
 #include "logger.h"
 #include "path.h"
 #include "util.h"
@@ -698,7 +697,7 @@
 
 			try {
 				event.exec(*pair.second);
-			} catch (const js::ErrorInfo &info) {
+			} catch (const duk::ErrorInfo &info) {
 				log::warning() << "plugin " << pair.second->info().name << ": error: " << info.what() << std::endl;
 
 				if (!info.fileName.empty())
@@ -850,7 +849,7 @@
 	plugin->onTimerEnd.connect(std::bind(&Irccd::handleTimerEnd, this, ptr, _1));
 
 	/* Store reference to irccd */
-	plugin->context().putGlobal("\xff""\xff""irccd", js::RawPointer<Irccd>{this});
+	duk::putGlobal(plugin->context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{this});
 
 	/* Initial load now */
 	try {
@@ -934,12 +933,12 @@
 
 		auto &ctx = plugin->context();
 
-		js::StackAssert sa(ctx);
+		duk::StackAssert sa(ctx);
 
 		try {
-			ctx.getGlobal<void>("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
-			ctx.pcall(0);
-			ctx.pop();
+			duk::getGlobal<void>(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
+			duk::pcall(ctx, 0);
+			duk::pop(ctx);
 		} catch (const std::exception &) {
 			log::info() << "failure" << std::endl;
 		}
--- a/lib/irccd/irccd.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/irccd.h	Wed Mar 30 13:52:47 2016 +0200
@@ -29,7 +29,7 @@
 
 #include <irccd-config.h>
 
-#include <irccd/private/sockets.h>
+#include "sockets.h"
 
 #if defined(WITH_JS)
 #  include "plugin.h"
--- a/lib/irccd/irccdctl.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/irccdctl.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -25,19 +25,17 @@
 
 #include <irccd-config.h>
 
-#include <irccd/private/elapsed-timer.h>
-#include <irccd/private/filesystem.h>
-#include <irccd/private/ini.h>
-#include <irccd/private/sockets.h>
-
-#include <irccd/json.h>
-#include <irccd/logger.h>
-#include <irccd/options.h>
-#include <irccd/path.h>
-#include <irccd/system.h>
-#include <irccd/util.h>
-
+#include "elapsed-timer.h"
+#include "filesystem.h"
+#include "ini.h"
 #include "irccdctl.h"
+#include "json.h"
+#include "logger.h"
+#include "options.h"
+#include "path.h"
+#include "sockets.h"
+#include "system.h"
+#include "util.h"
 
 namespace irccd {
 
--- a/lib/irccd/irccdctl.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/irccdctl.h	Wed Mar 30 13:52:47 2016 +0200
@@ -24,10 +24,9 @@
 #include <memory>
 #include <string>
 
-#include "private/connection.h"
-
 #include "alias.h"
 #include "application.h"
+#include "connection.h"
 #include "options.h"
 
 namespace irccd {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-directory.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,376 @@
+/*
+ * js-directory.cpp -- Irccd.Directory API
+ *
+ * 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 <cerrno>
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <regex>
+#include <stdexcept>
+#include <string>
+
+#include <irccd-config.h>
+
+#include "directory.h"
+#include "filesystem.h"
+#include "js.h"
+#include "js-irccd.h"
+#include "path.h"
+
+namespace irccd {
+
+namespace {
+
+std::string path(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::This{});
+	duk::getProperty<void>(ctx, -1, "path");
+
+	if (duk::type(ctx, -1) != DUK_TYPE_STRING)
+		duk::raise(ctx, duk::TypeError("invalid this binding"));
+
+	std::string ret = duk::get<std::string>(ctx, -1);
+
+	if (ret.empty())
+		duk::raise(ctx, duk::TypeError("invalid directory with empty path"));
+
+	duk::pop(ctx, 2);
+
+	return ret;
+}
+
+/*
+ * Find an entry recursively (or not) in a directory using a predicate
+ * which can be used to test for regular expression, equality.
+ *
+ * Do not use this function directly, use:
+ *
+ * - findName
+ * - findRegex
+ */
+template <typename Pred>
+std::string findPath(const std::string &base, bool recursive, Pred pred)
+{
+	/*
+	 * For performance reason, we first iterate over all entries that are
+	 * not directories to avoid going deeper recursively if the requested
+	 * file is in the current directory.
+	 */
+	Directory directory(base);
+
+	for (const DirectoryEntry &entry : directory)
+		if (entry.type != DirectoryEntry::Dir && pred(entry.name))
+			return base + entry.name;
+
+	if (!recursive)
+		throw std::out_of_range("entry not found");
+
+	for (const DirectoryEntry &entry : directory) {
+		if (entry.type == DirectoryEntry::Dir) {
+			std::string next = base + entry.name + fs::Separator;
+			std::string path = findPath(next, true, pred);
+
+			if (!path.empty())
+				return path;
+		}
+	}
+
+	return "";
+}
+
+/*
+ * Helper for finding by equality.
+ */
+std::string findName(std::string base, const std::string &pattern, bool recursive)
+{
+	return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
+		return pattern == entryname;
+	});
+}
+
+/*
+ * Helper for finding by regular expression
+ */
+std::string findRegex(const std::string &base, std::string pattern, bool recursive)
+{
+	std::regex regexp(pattern, std::regex::ECMAScript);
+	std::smatch smatch;
+
+	return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
+		return std::regex_match(entryname, smatch, regexp);
+	});
+}
+
+/*
+ * Generic find function for:
+ *
+ * - Directory.find
+ * - Directory.prototype.find
+ *
+ * The patternIndex is the argument where to test if the argument is a regex or a string.
+ */
+duk::Ret find(duk::ContextPtr ctx, std::string base, bool recursive, int patternIndex)
+{
+	base = path::clean(base);
+
+	try {
+		std::string path;
+
+		if (duk::is<std::string>(ctx, patternIndex)) {
+			path = findName(base, duk::get<std::string>(ctx, patternIndex), recursive);
+		} else {
+			/* Check if it's a valid RegExp object */
+			duk::getGlobal<void>(ctx, "RegExp");
+
+			bool isRegex = duk::instanceof(ctx, patternIndex, -1);
+
+			duk::pop(ctx);
+
+			if (isRegex)
+				path = findRegex(base, duk::getProperty<std::string>(ctx, patternIndex, "source"), recursive);
+			else
+				duk::raise(ctx, duk::TypeError("pattern must be a string or a regex expression"));
+		}
+
+		if (path.empty())
+			return 0;
+
+		duk::push(ctx, path);
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, duk::Error(ex.what()));
+	}
+
+	return 1;
+}
+
+/*
+ * Generic remove function for:
+ *
+ * - Directory.remove
+ * - Directory.prototype.remove
+ */
+duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive)
+{
+	if (!recursive) {
+		::remove(path.c_str());
+	} else {
+		try {
+			Directory directory(path);
+
+			for (const DirectoryEntry &entry : directory) {
+				if (entry.type == DirectoryEntry::Dir) {
+					(void)remove(ctx, path + fs::Separator + entry.name, true);
+				} else {
+					std::string filename = path + fs::Separator + entry.name;
+
+					::remove(filename.c_str());
+				}
+			}
+
+			::remove(path.c_str());
+		} catch (const std::exception &) {
+			// TODO: put the error in a log.
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Method: Directory.find(pattern, recursive)
+ * --------------------------------------------------------
+ *
+ * Synonym of Directory.find(path, pattern, recursive) but the path is taken
+ * from the directory object.
+ *
+ * Arguments:
+ *   - pattern, the regular expression or file name,
+ *   - recursive, set to true to search recursively (default: false).
+ * Returns:
+ *   The path to the file or undefined on errors or not found
+ */
+duk::Ret methodFind(duk::ContextPtr ctx)
+{
+	return find(ctx, path(ctx), duk::optional<bool>(ctx, 1, false), 0);
+}
+
+/*
+ * Method: Directory.remove(recursive)
+ * --------------------------------------------------------
+ *
+ * Synonym of Directory.remove(recursive) but the path is taken from the
+ * directory object.
+ *
+ * Arguments:
+ *   - recursive, recursively or not (default: false).
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodRemove(duk::ContextPtr ctx)
+{
+	return remove(ctx, path(ctx), duk::optional<bool>(ctx, 0, false));
+}
+
+const duk::FunctionMap methods{
+	{ "find",		{ methodFind,		DUK_VARARGS	} },
+	{ "remove",		{ methodRemove,		1		} }
+};
+
+/* --------------------------------------------------------
+ * Directory "static" functions
+ * -------------------------------------------------------- */
+
+/*
+ * Function: Irccd.Directory(path, flags) [constructor]
+ * --------------------------------------------------------
+ *
+ * Opens and read the directory at the specified path.
+ *
+ * Arguments:
+ *   - path, the path to the directory,
+ *   - flags, the optional flags (default: 0).
+ * Throws:
+ *   - Any exception on error
+ */
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	if (!duk_is_constructor_call(ctx))
+		return 0;
+
+	try {
+		Directory directory(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0));
+
+		duk::push(ctx, duk::This{});
+		duk::push(ctx, "count");
+		duk::push(ctx, directory.count());
+		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
+		duk::push(ctx, "path");
+		duk::push(ctx, duk::require<std::string>(ctx, 0));
+		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
+		duk::push(ctx, "entries");
+		duk::push(ctx, duk::Array{});
+
+		int i = 0;
+		for (const DirectoryEntry &entry : directory) {
+			duk::push(ctx, duk::Object{});
+			duk::putProperty(ctx, -1, "name", entry.name);
+			duk::putProperty(ctx, -1, "type", static_cast<int>(entry.type));
+			duk::putProperty(ctx, -2, i++);
+		}
+
+		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, SystemError(errno, ex.what()));
+	}
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.Directory.find(path, pattern, recursive)
+ * --------------------------------------------------------
+ *
+ * Find an entry by a pattern or a regular expression.
+ *
+ * Arguments:
+ *   - path, the base path,
+ *   - pattern, the regular expression or file name,
+ *   - recursive, set to true to search recursively (default: false).
+ * Returns:
+ *   The path to the file or undefined on errors or not found.
+ */
+duk::Ret funcFind(duk::ContextPtr ctx)
+{
+	return find(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 2, false), 1);
+}
+
+/*
+ * Function: Irccd.Directory.remove(path, recursive)
+ * --------------------------------------------------------
+ *
+ * Remove the directory optionally recursively.
+ *
+ * Arguments:
+ *   - path, the path to the directory,
+ *   - recursive, recursively or not (default: false).
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret funcRemove(duk::ContextPtr ctx)
+{
+	return remove(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 1, false));
+}
+
+/*
+ * Function: Irccd.Directory.mkdir(path, mode = 0700)
+ * --------------------------------------------------------
+ *
+ * Create a directory specified by path. It will create needed subdirectories
+ * just like you have invoked mkdir -p.
+ *
+ * Arguments:
+ *   - path, the path to the directory,
+ *   - mode, the mode, not available on all platforms.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret funcMkdir(duk::ContextPtr ctx)
+{
+	try {
+		fs::mkdir(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0700));
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, SystemError(errno, ex.what()));
+	}
+
+	return 0;
+}
+
+const duk::FunctionMap functions{
+	{ "find",		{ funcFind,	DUK_VARARGS } },
+	{ "mkdir",		{ funcMkdir,	DUK_VARARGS } },
+	{ "remove",		{ funcRemove,	DUK_VARARGS } }
+};
+
+const duk::Map<int> constants{
+	{ "Dot",		static_cast<int>(Directory::Dot)		},
+	{ "DotDot",		static_cast<int>(Directory::DotDot)		},
+	{ "TypeUnknown",	static_cast<int>(DirectoryEntry::Unknown)	},
+	{ "TypeDir",		static_cast<int>(DirectoryEntry::Dir)		},
+	{ "TypeFile",		static_cast<int>(DirectoryEntry::File)		},
+	{ "TypeLink",		static_cast<int>(DirectoryEntry::Link)		}
+};
+
+} // !namespace
+
+void loadJsDirectory(duk::ContextPtr ctx) noexcept
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Function{constructor, 2});
+	duk::push(ctx, constants);
+	duk::push(ctx, functions);
+	duk::putProperty(ctx, -1, "separator", std::string{fs::Separator});
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, methods);
+	duk::putProperty(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "Directory");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-directory.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,31 @@
+/*
+ * js-directory.h -- Irccd.Directory API
+ *
+ * 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_JS_DIRECTORY_H
+#define IRCCD_JS_DIRECTORY_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsDirectory(duk::ContextPtr ctx) noexcept;
+
+} // !irccd
+
+#endif // !_IRCCD_JS_DIRECTORY_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-elapsed-timer.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,134 @@
+/*
+ * js-elapsed-timer.cpp -- Irccd.ElapsedTimer API
+ *
+ * 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 "elapsed-timer.h"
+#include "js.h"
+
+namespace irccd {
+
+namespace duk {
+
+template <>
+class TypeTraits<ElapsedTimer> {
+public:
+	static inline std::string name()
+	{
+		return "\xff""\xff""ElapsedTimer";
+	}
+
+	static inline std::vector<std::string> inherits()
+	{
+		return {};
+	}
+};
+
+} // !duk
+
+namespace {
+
+/*
+ * Method: ElapsedTimer.pause
+ * ------------------------------------------------------------------
+ *
+ * Pause the timer, without resetting the current elapsed time stored.
+ */
+duk::Ret pause(duk::ContextPtr ctx)
+{
+	duk::self<duk::Pointer<ElapsedTimer>>(ctx)->pause();
+
+	return 0;
+}
+
+/*
+ * Method: ElapsedTimer.reset
+ * ------------------------------------------------------------------
+ *
+ * Reset the elapsed time to 0, the status is not modified.
+ */
+duk::Ret reset(duk::ContextPtr ctx)
+{
+	duk::self<duk::Pointer<ElapsedTimer>>(ctx)->reset();
+
+	return 0;
+}
+
+/*
+ * Method: ElapsedTimer.restart
+ * ------------------------------------------------------------------
+ *
+ * Restart the timer without resetting the current elapsed time.
+ */
+duk::Ret restart(duk::ContextPtr ctx)
+{
+	duk::self<duk::Pointer<ElapsedTimer>>(ctx)->restart();
+
+	return 0;
+}
+
+/*
+ * Method: ElapsedTimer.elapsed
+ * ------------------------------------------------------------------
+ *
+ * Get the number of elapsed milliseconds.
+ *
+ * Returns:
+ *   The time elapsed.
+ */
+duk::Ret elapsed(duk::ContextPtr ctx)
+{
+	duk::push(ctx, (int)duk::self<duk::Pointer<ElapsedTimer>>(ctx)->elapsed());
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.ElapsedTimer() [constructor]
+ * ------------------------------------------------------------------
+ *
+ * Construct a new ElapsedTimer object.
+ */
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	duk::construct(ctx, duk::Pointer<ElapsedTimer>{new ElapsedTimer});
+
+	return 0;
+}
+
+const duk::FunctionMap methods{
+	{ "elapsed",	{ elapsed,	0 } },
+	{ "pause",	{ pause,	0 } },
+	{ "reset",	{ reset,	0 } },
+	{ "restart",	{ restart,	0 } }
+};
+
+} // !namespace
+
+void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Function{constructor, 0});
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, methods);
+	duk::putProperty(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "ElapsedTimer");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-elapsed-timer.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,31 @@
+/*
+ * js-elapsed-timer.h -- Irccd.ElapsedTimer API
+ *
+ * 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_JS_ELAPSED_TIMER_H
+#define IRCCD_JS_ELAPSED_TIMER_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept;
+
+} // !irccd
+
+#endif // !IRCCD_JS_ELAPSED_TIMER_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-file.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,622 @@
+/*
+ * js-file.h -- Irccd.File API
+ *
+ * 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 <cerrno>
+#include <cstring>
+#include <fstream>
+
+#include <irccd-config.h>
+
+#if defined(HAVE_STAT)
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#endif
+
+#include "filesystem.h"
+#include "js-irccd.h"
+#include "js-file.h"
+
+#if defined(HAVE_STAT)
+
+namespace irccd {
+
+/*
+ * duk::File object for Javascript I/O
+ * ------------------------------------------------------------------
+ */
+
+File::File(std::string path, const std::string &mode)
+	: m_path(std::move(path))
+	, m_destructor([] (std::FILE *fp) { std::fclose(fp); })
+{
+	if ((m_stream = std::fopen(m_path.c_str(), mode.c_str())) == nullptr)
+		throw std::runtime_error(std::strerror(errno));
+}
+
+File::File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept
+	: m_stream(fp)
+	, m_destructor(std::move(destructor))
+{
+	assert(m_destructor != nullptr);
+}
+
+File::~File() noexcept
+{
+	close();
+}
+
+void File::close() noexcept
+{
+	if (m_stream) {
+		m_destructor(m_stream);
+		m_stream = nullptr;
+	}
+}
+
+bool File::isClosed() noexcept
+{
+	return m_stream == nullptr;
+}
+
+void File::seek(long amount, long dir)
+{
+	if (std::fseek(m_stream, amount, dir) != 0)
+		throw std::runtime_error(std::strerror(errno));
+}
+
+unsigned File::tell()
+{
+	long pos = std::ftell(m_stream);
+
+	if (pos == -1L)
+		throw std::runtime_error(std::strerror(errno));
+
+	return pos;
+}
+
+std::string File::readline()
+{
+	std::string result;
+	int ch;
+
+	while ((ch = std::fgetc(m_stream)) != EOF && ch != '\n')
+		result += ch;
+
+	if (ch == EOF && std::ferror(m_stream))
+		throw std::runtime_error(std::strerror(errno));
+
+	return result;
+}
+
+std::string File::read(int amount)
+{
+	assert(amount != 0);
+
+	std::string result;
+	int ch;
+
+	for (int i = 0; (ch = std::fgetc(m_stream)) != EOF; ) {
+		result += ch;
+
+		if (amount > 0 && ++i == amount)
+			break;
+	}
+
+	if (ch == EOF && std::ferror(m_stream))
+		throw std::runtime_error(std::strerror(errno));
+
+	return result;
+}
+
+void File::write(const std::string &data)
+{
+	if (std::fwrite(data.c_str(), data.length(), 1, m_stream) != 1)
+		throw std::runtime_error(std::strerror(errno));
+}
+
+bool File::eof() const noexcept
+{
+	return std::feof(m_stream);
+}
+
+/*
+ * duk::TypeInfo specialization for struct stat
+ * ------------------------------------------------------------------
+ */
+
+namespace duk {
+
+template <>
+class TypeTraits<struct stat> {
+public:
+	static void push(ContextPtr ctx, const struct stat &st)
+	{
+		duk::push(ctx, Object{});
+
+#if defined(HAVE_STAT_ST_ATIME)
+		duk::putProperty(ctx, -2, "atime", static_cast<int>(st.st_atime));
+#endif
+#if defined(HAVE_STAT_ST_BLKSIZE)
+		duk::putProperty(ctx, -2, "blksize", static_cast<int>(st.st_blksize));
+#endif
+#if defined(HAVE_STAT_ST_BLOCKS)
+		duk::putProperty(ctx, -2, "blocks", static_cast<int>(st.st_blocks));
+#endif
+#if defined(HAVE_STAT_ST_CTIME)
+		duk::putProperty(ctx, -2, "ctime", static_cast<int>(st.st_ctime));
+#endif
+#if defined(HAVE_STAT_ST_DEV)
+		duk::putProperty(ctx, -2, "dev", static_cast<int>(st.st_dev));
+#endif
+#if defined(HAVE_STAT_ST_GID)
+		duk::putProperty(ctx, -2, "gid", static_cast<int>(st.st_gid));
+#endif
+#if defined(HAVE_STAT_ST_INO)
+		duk::putProperty(ctx, -2, "ino", static_cast<int>(st.st_ino));
+#endif
+#if defined(HAVE_STAT_ST_MODE)
+		duk::putProperty(ctx, -2, "mode", static_cast<int>(st.st_mode));
+#endif
+#if defined(HAVE_STAT_ST_MTIME)
+		duk::putProperty(ctx, -2, "mtime", static_cast<int>(st.st_mtime));
+#endif
+#if defined(HAVE_STAT_ST_NLINK)
+		duk::putProperty(ctx, -2, "nlink", static_cast<int>(st.st_nlink));
+#endif
+#if defined(HAVE_STAT_ST_RDEV)
+		duk::putProperty(ctx, -2, "rdev", static_cast<int>(st.st_rdev));
+#endif
+#if defined(HAVE_STAT_ST_SIZE)
+		duk::putProperty(ctx, -2, "size", static_cast<int>(st.st_size));
+#endif
+#if defined(HAVE_STAT_ST_UID)
+		duk::putProperty(ctx, -2, "uid", static_cast<int>(st.st_uid));
+#endif
+	}
+};
+
+} // !duk
+
+#endif // !HAVE_STAT
+
+namespace {
+
+/* --------------------------------------------------------
+ * File methods
+ * -------------------------------------------------------- */
+
+/*
+ * Method: File.basename()
+ * --------------------------------------------------------
+ *
+ * Synonym of `Irccd.File.basename(path)` but with the path from the file.
+ *
+ * Returns:
+ *   The base name.
+ */
+duk::Ret methodBasename(duk::ContextPtr ctx)
+{
+	duk::push(ctx, fs::baseName(duk::self<duk::Pointer<File>>(ctx)->path()));
+
+	return 1;
+}
+
+/*
+ * Method: File.close()
+ * --------------------------------------------------------
+ *
+ * Force close of the file, automatically called when object is collected.
+ */
+duk::Ret methodClose(duk::ContextPtr ctx)
+{
+	duk::self<duk::Pointer<File>>(ctx)->close();
+
+	return 0;
+}
+
+/*
+ * Method: File.dirname()
+ * --------------------------------------------------------
+ *
+ * Synonym of `Irccd.File.dirname(path)` but with the path from the file.
+ *
+ * Returns:
+ *   The directory name.
+ */
+duk::Ret methodDirname(duk::ContextPtr ctx)
+{
+	duk::push(ctx, fs::dirName(duk::self<duk::Pointer<File>>(ctx)->path()));
+
+	return 1;
+}
+
+/*
+ * Method: File.read(amount)
+ * --------------------------------------------------------
+ *
+ * Read the specified amount of characters or the whole file.
+ *
+ * Arguments:
+ *   - amount, the amount of characters or -1 to read all (Optional, default: -1).
+ * Returns:
+ *   The string.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodRead(duk::ContextPtr ctx)
+{
+	auto amount = duk::optional<int>(ctx, 0, -1);
+	auto self = duk::self<duk::Pointer<File>>(ctx);
+
+	if (amount == 0 || self->isClosed())
+		return 0;
+
+	try {
+		duk::push(ctx, self->read(amount));
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 1;
+}
+
+/*
+ * Method: File.readline()
+ * --------------------------------------------------------
+ *
+ * Read the next line available.
+ *
+ * Returns:
+ *   The next line or undefined if eof.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodReadline(duk::ContextPtr ctx)
+{
+	try {
+		auto file = duk::self<duk::Pointer<File>>(ctx);
+
+		if (file->isClosed() || file->eof())
+			return 0;
+
+		duk::push(ctx, file->readline());
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 1;
+}
+
+/*
+ * Method: File.remove()
+ * --------------------------------------------------------
+ *
+ * Synonym of File.remove(path) but with the path from the file.
+ *
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodRemove(duk::ContextPtr ctx)
+{
+	if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0)
+		duk::raise(ctx, SystemError());
+
+	return 0;
+}
+
+/*
+ * Method: File.seek(type, amount)
+ * --------------------------------------------------------
+ *
+ * Sets the position in the file.
+ *
+ * Arguments:
+ *   - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet),
+ *   - amount, the new offset.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodSeek(duk::ContextPtr ctx)
+{
+	auto type = duk::require<int>(ctx, 0);
+	auto amount = duk::require<int>(ctx, 1);
+	auto file = duk::self<duk::Pointer<File>>(ctx);
+
+	if (file->isClosed())
+		return 0;
+
+	try {
+		file->seek(amount, type);
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 0;
+}
+
+#if defined(HAVE_STAT)
+
+/*
+ * Method: File.stat() [optional]
+ * --------------------------------------------------------
+ *
+ * Synonym of File.stat(path) but with the path from the file.
+ *
+ * Returns:
+ *   The stat information.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodStat(duk::ContextPtr ctx)
+{
+	struct stat st;
+	auto file = duk::self<duk::Pointer<File>>(ctx);
+
+	if (file->isClosed())
+		return 0;
+
+	if (::stat(file->path().c_str(), &st) < 0)
+		duk::raise(ctx, SystemError());
+
+	duk::push(ctx, st);
+
+	return 1;
+}
+
+#endif // !HAVE_STAT
+
+/*
+ * Method: File.tell()
+ * --------------------------------------------------------
+ *
+ * Get the actual position in the file.
+ *
+ * Returns:
+ *   The position.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodTell(duk::ContextPtr ctx)
+{
+	auto file = duk::self<duk::Pointer<File>>(ctx);
+
+	if (file->isClosed())
+		return 0;
+
+	try {
+		duk::push(ctx, static_cast<int>(file->tell()));
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 1;
+}
+
+/*
+ * Method: File.write(data)
+ * --------------------------------------------------------
+ *
+ * Write some characters to the file.
+ *
+ * Arguments:
+ *   - data, the character to write.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret methodWrite(duk::ContextPtr ctx)
+{
+	auto file = duk::self<duk::Pointer<File>>(ctx);
+
+	if (file->isClosed())
+		return 0;
+
+	try {
+		file->write(duk::require<std::string>(ctx, 0));
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 0;
+}
+
+const duk::FunctionMap methods{
+	{ "basename",	{ methodBasename,	0	} },
+	{ "close",	{ methodClose,		0	} },
+	{ "dirname",	{ methodDirname,	0	} },
+	{ "read",	{ methodRead,		1	} },
+	{ "readline",	{ methodReadline,	0	} },
+	{ "remove",	{ methodRemove,		0	} },
+	{ "seek",	{ methodSeek,		2	} },
+#if defined(HAVE_STAT)
+	{ "stat",	{ methodStat,		0	} },
+#endif
+	{ "tell",	{ methodTell,		0	} },
+	{ "write",	{ methodWrite,		1	} },
+};
+
+/* --------------------------------------------------------
+ * File "static" functions
+ * -------------------------------------------------------- */
+
+/*
+ * Function: Irccd.File(path, mode) [constructor]
+ * --------------------------------------------------------
+ *
+ * Open a file specified by path with the specified mode.
+ *
+ * Arguments:
+ *   - path, the path to the file,
+ *   - mode, the mode string.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	if (!duk_is_constructor_call(ctx))
+		return 0;
+
+	std::string path = duk::require<std::string>(ctx, 0);
+	std::string mode = duk::require<std::string>(ctx, 1);
+
+	try {
+		duk::construct(ctx, duk::Pointer<File>{new File(path, mode)});
+	} catch (const std::exception &) {
+		duk::raise(ctx, SystemError());
+	}
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.File.basename(path)
+ * --------------------------------------------------------
+ *
+ * Return the file basename as specified in `basename(3)` C function.
+ *
+ * Arguments:
+ *   - path, the path to the file.
+ * Returns:
+ *   The base name.
+ */
+duk::Ret functionBasename(duk::ContextPtr ctx)
+{
+	duk::push(ctx, fs::baseName(duk::require<std::string>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.File.dirname(path)
+ * --------------------------------------------------------
+ *
+ * Return the file directory name as specified in `dirname(3)` C function.
+ *
+ * Arguments:
+ *   - path, the path to the file.
+ * Returns:
+ *   The directory name.
+ */
+duk::Ret functionDirname(duk::ContextPtr ctx)
+{
+	duk::push(ctx, fs::dirName( duk::require<std::string>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.File.exists(path)
+ * --------------------------------------------------------
+ *
+ * Check if the file exists.
+ *
+ * Arguments:
+ *   - path, the path to the file.
+ * Returns:
+ *   True if exists.
+ * Throws:
+ *   - Any exception if we don't have access.
+ */
+duk::Ret functionExists(duk::ContextPtr ctx)
+{
+	duk::push(ctx, fs::exists(duk::require<std::string>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * function Irccd.File.remove(path)
+ * --------------------------------------------------------
+ *
+ * Remove the file at the specified path.
+ *
+ * Arguments:
+ *   - path, the path to the file.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret functionRemove(duk::ContextPtr ctx)
+{
+	if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0)
+		duk::raise(ctx, SystemError());
+
+	return 0;
+}
+
+#if defined(HAVE_STAT)
+
+/*
+ * function Irccd.File.stat(path) [optional]
+ * --------------------------------------------------------
+ *
+ * Get file information at the specified path.
+ *
+ * Arguments:
+ *   - path, the path to the file.
+ * Returns:
+ *   The stat information.
+ * Throws:
+ *   - Any exception on error.
+ */
+duk::Ret functionStat(duk::ContextPtr ctx)
+{
+	struct stat st;
+
+	if (::stat(duk::require<std::string>(ctx, 0).c_str(), &st) < 0)
+		duk::raise(ctx, SystemError());
+
+	duk::push(ctx, st);
+
+	return 1;
+}
+
+#endif // !HAVE_STAT
+
+const duk::FunctionMap functions{
+	{ "basename",	{ functionBasename,	1			} },
+	{ "dirname",	{ functionDirname,	1			} },
+	{ "exists",	{ functionExists,	1			} },
+	{ "remove",	{ functionRemove,	1			} },
+#if defined(HAVE_STAT)
+	{ "stat",	{ functionStat,		1			} },
+#endif
+};
+
+const duk::Map<int> constants{
+	{ "SeekCur",	static_cast<int>(std::fstream::cur)	},
+	{ "SeekEnd",	static_cast<int>(std::fstream::end)	},
+	{ "SeekSet",	static_cast<int>(std::fstream::beg)	},
+};
+
+} // !namespace
+
+void loadJsFile(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Function{constructor, 2});
+	duk::push(ctx, constants);
+	duk::push(ctx, functions);
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, methods);
+	duk::putProperty(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "File");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-file.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,185 @@
+/*
+ * js-file.h -- Irccd.File API
+ *
+ * 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_JS_FILE_H
+#define IRCCD_JS_FILE_H
+
+#include <cstdio>
+
+#include "js.h"
+
+namespace irccd {
+
+/**
+ * @class File
+ * @brief Object for Javascript to perform I/O.
+ *
+ * This class can be constructed to Javascript.
+ *
+ * It is used in:
+ *
+ * - Irccd.File [constructor]
+ * - Irccd.System.popen (optional)
+ */
+class File {
+private:
+	File(const File &) = delete;
+	File &operator=(const File &) = delete;
+
+	File(File &&) = delete;
+	File &operator=(File &&) = delete;
+
+protected:
+	std::string m_path;
+	std::FILE *m_stream;
+	std::function<void (std::FILE *)> m_destructor;
+
+public:
+	/**
+	 * Construct a file specified by path
+	 *
+	 * @param path the path
+	 * @param mode the mode string (for std::fopen)
+	 * @throw std::runtime_error on failures
+	 */
+	File(std::string path, const std::string &mode);
+
+	/**
+	 * Construct a file from a already created FILE pointer (e.g. popen).
+	 *
+	 * The class takes ownership of fp and will close it.
+	 *
+	 * @pre destructor must not be null
+	 * @param fp the file pointer
+	 */
+	File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept;
+
+	/**
+	 * Closes the file.
+	 */
+	virtual ~File() noexcept;
+
+	/**
+	 * Get the path.
+	 *
+	 * @return the path
+	 * @warning empty when constructed from the FILE constructor
+	 */
+	inline const std::string &path() const noexcept
+	{
+		return m_path;
+	}
+
+	/**
+	 * Force close, can be safely called multiple times.
+	 */
+	void close() noexcept;
+
+	/**
+	 * Tells if the file was closed.
+	 *
+	 * @return true if closed
+	 */
+	bool isClosed() noexcept;
+
+	/**
+	 * std::fseek wrapper.
+	 *
+	 * @param offset the offset
+	 * @param origin the origin (SEEK_SET, *)
+	 * @throw std::runtime_error on failure
+	 */
+	void seek(long offset, long origin);
+
+	/**
+	 * std::ftell wrapper.
+	 *
+	 * @return the position
+	 * @throw std::runtime_error on failure
+	 */
+	unsigned tell();
+
+	/**
+	 * Read until the next line and discards the \\n character.
+	 *
+	 * @return the next line or empty if EOF
+	 * @throw std::runtime_error on failure
+	 */
+	std::string readline();
+
+	/**
+	 * Read the specified amount of characters.
+	 *
+	 * If amount is less than 0, the maximum is read.
+	 *
+	 * @pre amount != 0
+	 * @param amount the number of characters to read
+	 * @return the read string
+	 * @throw std::runtime_error on failure
+	 */
+	std::string read(int amount = -1);
+
+	/**
+	 * Write the string to the file.
+	 *
+	 * @param data the data to write
+	 * @throw std::runtime_error on failure
+	 */
+	void write(const std::string &data);
+
+	/**
+	 * Check if the file reached the end.
+	 *
+	 * @return true if eof
+	 */
+	bool eof() const noexcept;
+};
+
+namespace duk {
+
+template <>
+class TypeTraits<File> {
+public:
+	static inline void prototype(ContextPtr ctx)
+	{
+		getGlobal<void>(ctx, "Irccd");
+		getGlobal<void>(ctx, "File");
+		getProperty<void>(ctx, -1, "prototype");
+		remove(ctx, -2);
+		remove(ctx, -2);
+	}
+
+	static inline std::string name()
+	{
+		return "\xff""\xff""File";
+	}
+
+	static inline std::vector<std::string> inherits()
+	{
+		return {};
+	}
+};
+
+} // !duk
+
+void loadJsFile(duk::ContextPtr ctx);
+
+} // !irccd
+
+#endif // !IRCCD_JS_FILE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-irccd.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,89 @@
+/*
+ * js-irccd.cpp -- Irccd API
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd-config.h>
+
+#include "js-irccd.h"
+
+namespace irccd {
+
+SystemError::SystemError()
+	: m_errno(errno)
+	, m_message(std::strerror(m_errno))
+{
+}
+
+SystemError::SystemError(int e, std::string message)
+	: m_errno(e)
+	, m_message(std::move(message))
+{
+}
+
+void SystemError::raise(duk::ContextPtr ctx) const
+{
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::getProperty<void>(ctx, -1, "SystemError");
+	duk::push(ctx, m_errno);
+	duk::push(ctx, m_message);
+	duk::create(ctx, 2);
+	duk::remove(ctx, -2);
+}
+
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::This{});
+	duk::putProperty(ctx, -1, "errno", duk::require<int>(ctx, 0));
+	duk::putProperty(ctx, -1, "message", duk::require<std::string>(ctx, 1));
+	duk::putProperty(ctx, -1, "name", "SystemError");
+	duk::pop(ctx);
+
+	return 0;
+}
+
+void loadJsIrccd(duk::Context &ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	/* Irccd */
+	duk::push(ctx, duk::Object{});
+
+	/* Version */
+	duk::push(ctx, duk::Object{});
+	duk::putProperty(ctx, -1, "major", IRCCD_VERSION_MAJOR);
+	duk::putProperty(ctx, -1, "minor", IRCCD_VERSION_MINOR);
+	duk::putProperty(ctx, -1, "patch", IRCCD_VERSION_PATCH);
+	duk::putProperty(ctx, -2, "version");
+
+	/* Create the SystemError that inherits from Error */
+	duk::push(ctx, duk::Function{constructor, 2});
+	duk::getGlobal<void>(ctx, "Error");
+	duk::push(ctx, duk::Function{[] (duk::ContextPtr) -> duk::Ret { return 0; }});
+	duk::getProperty<void>(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "prototype");
+	duk::create(ctx, 0);
+	duk::putProperty(ctx, -3, "prototype");
+	duk::pop(ctx);
+	duk::dup(ctx, -1);
+	duk::putProperty(ctx, -2, "constructor");
+	duk::putProperty(ctx, -2, "SystemError");
+
+	/* Set Irccd as global */
+	duk::putGlobal(ctx, "Irccd");
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-irccd.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,51 @@
+/*
+ * js-irccd.h -- Irccd API
+ *
+ * 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_JS_IRCCD_H
+#define IRCCD_JS_IRCCD_H
+
+#include <cerrno>
+#include <cstring>
+
+#include "js.h"
+
+namespace irccd {
+
+class SystemError {
+private:
+	int m_errno;
+	std::string m_message;
+
+public:
+	SystemError();
+
+	SystemError(int e, std::string message);
+
+	void raise(duk::ContextPtr ctx) const;
+
+	std::string name() const
+	{
+		return "SystemError";
+	}
+};
+
+void loadJsIrccd(duk::Context &);
+
+} // !irccd
+
+#endif // !IRCCD_JS_IRCCD_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-logger.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,99 @@
+/*
+ * js-logger.h -- Irccd.Logger API
+ *
+ * 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 "js-logger.h"
+#include "logger.h"
+
+namespace irccd {
+
+namespace {
+
+duk::Ret print(duk::ContextPtr ctx, std::ostream &out)
+{
+	/*
+	 * Get the message before we start printing stuff to avoid
+	 * empty lines.
+	 */
+	out << "plugin " << duk::getGlobal<std::string>(ctx, "\xff""\xff""name");
+	out << ": " << duk::require<std::string>(ctx, 0) << std::endl;
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.Logger.info(message)
+ * --------------------------------------------------------
+ *
+ * Write a verbose message.
+ *
+ * Arguments:
+ *   - message, the message.
+ */
+duk::Ret info(duk::ContextPtr ctx)
+{
+	return print(ctx, log::info());
+}
+
+/*
+ * Function: Irccd.Logger.warning(message)
+ * --------------------------------------------------------
+ *
+ * Write a warning message.
+ *
+ * Arguments:
+ *   - message, the warning.
+ */
+duk::Ret warning(duk::ContextPtr ctx)
+{
+	return print(ctx, log::warning());
+}
+
+/*
+ * Function: Logger.debug(message)
+ * --------------------------------------------------------
+ *
+ * Write a debug message, only shown if irccd is compiled in debug.
+ *
+ * Arguments:
+ *   - message, the message.
+ */
+duk::Ret debug(duk::ContextPtr ctx)
+{
+	return print(ctx, log::debug());
+}
+
+const duk::FunctionMap functions{
+	{ "info",	{ info,		1 } },
+	{ "warning",	{ warning,	1 } },
+	{ "debug",	{ debug,	1 } }
+};
+
+} // !namespace
+
+void loadJsLogger(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, functions);
+	duk::putProperty(ctx, -2, "Logger");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-logger.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * js-logger.h -- Irccd.Logger API
+ *
+ * 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_JS_LOGGER_H
+#define IRCCD_JS_LOGGER_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsLogger(duk::ContextPtr ctx);
+
+} // !irccd
+
+#endif // !IRCCD_JS_LOGGER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-plugin.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,183 @@
+/*
+ * js-plugin.cpp -- Irccd.Plugin API
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "irccd.h"
+#include "js-plugin.h"
+
+namespace irccd {
+
+namespace {
+
+/*
+ * Wrap function for these functions because they all takes the same arguments.
+ *
+ * - load,
+ * - reload,
+ * - unload.
+ */
+template <typename Func>
+duk::Ret wrap(duk::ContextPtr ctx, int nret, Func &&func)
+{
+	std::string name = duk::require<std::string>(ctx, 0);
+
+	try {
+		func(*duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"), name);
+	} catch (const std::out_of_range &ex) {
+		duk::raise(ctx, duk::ReferenceError(ex.what()));
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, duk::Error(ex.what()));
+	}
+
+	return nret;
+}
+
+/*
+ * Function: Irccd.Plugin.info([name])
+ * ------------------------------------------------------------------
+ *
+ * Get information about a plugin.
+ *
+ * The returned object as the following properties:
+ *
+ * - name: (string) the plugin identifier,
+ * - author: (string) the author,
+ * - license: (string) the license,
+ * - summary: (string) a short description,
+ * - version: (string) the version
+ *
+ * Arguments:
+ *   - name, the plugin identifier, if not specified the current plugin is selected.
+ * Returns:
+ *   The plugin information or undefined if the plugin was not found.
+ */
+duk::Ret info(duk::ContextPtr ctx)
+{
+	if (duk::top(ctx) >= 1) {
+		try {
+			duk::push(ctx, duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->requirePlugin(duk::require<std::string>(ctx, 0))->info());
+		} catch (...) {
+			duk::push(ctx, duk::Undefined{});
+		}
+	} else {
+		duk::push(ctx, duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin")->info());
+	}
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Plugin.list()
+ * ------------------------------------------------------------------
+ *
+ * Get the list of plugins, the array returned contains all plugin names.
+ *
+ * Returns:
+ *   The list of all plugin names.
+ */
+duk::Ret list(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::Array{});
+
+	int i = 0;
+	for (const auto &pair : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->plugins())
+		duk::putProperty(ctx, -1, i++, pair.first);
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Plugin.load(name)
+ * ------------------------------------------------------------------
+ *
+ * Load a plugin by name. This function will search through the standard directories.
+ *
+ * Arguments:
+ *   - name, the plugin identifier.
+ * Throws:
+ *   - Error on errors,
+ *   - ReferenceError if the plugin was not found.
+ */
+duk::Ret load(duk::ContextPtr ctx)
+{
+	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
+		irccd.loadPlugin(name, name, true);
+	});
+}
+
+/*
+ * Function: Irccd.Plugin.reload(name)
+ * ------------------------------------------------------------------
+ *
+ * Reload a plugin by name.
+ *
+ * Arguments:
+ *   - name, the plugin identifier.
+ * Throws:
+ *   - Error on errors,
+ *   - ReferenceError if the plugin was not found.
+ */
+duk::Ret reload(duk::ContextPtr ctx)
+{
+	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
+		irccd.reloadPlugin(name);
+	});
+}
+
+/*
+ * Function: Irccd.Plugin.unload(name)
+ * ------------------------------------------------------------------
+ *
+ * Unload a plugin by name.
+ *
+ * Arguments:
+ *   - name, the plugin identifier.
+ * Throws:
+ *   - Error on errors,
+ *   - ReferenceError if the plugin was not found.
+ */
+duk::Ret unload(duk::ContextPtr ctx)
+{
+	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
+		irccd.unloadPlugin(name);
+	});
+}
+
+const duk::FunctionMap functions{
+	{ "info",	{ info,		DUK_VARARGS	} },
+	{ "list",	{ list,		0		} },
+	{ "load",	{ load,		1		} },
+	{ "reload",	{ reload,	1		} },
+	{ "unload",	{ unload,	1		} }
+};
+
+} // !namespace
+
+void loadJsPlugin(duk::Context &ctx) noexcept
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, functions);
+	duk::push(ctx, duk::Object{});
+	duk::putProperty(ctx, -2, "config");
+	duk::putProperty(ctx, -2, "Plugin");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-plugin.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,31 @@
+/*
+ * js-plugin.h -- Irccd.Plugin API
+ *
+ * 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_JS_PLUGIN_H
+#define IRCCD_JS_PLUGIN_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsPlugin(duk::Context &ctx) noexcept;
+
+} // !irccd
+
+#endif // !IRCCD_JS_PLUGIN_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-server.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,530 @@
+/*
+ * js-server.cpp -- Irccd.Server API
+ *
+ * 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 <sstream>
+#include <unordered_map>
+
+#include "irccd.h"
+#include "js-server.h"
+#include "server.h"
+
+namespace irccd {
+
+namespace {
+
+/*
+ * Method: Server.cmode(channel, mode)
+ * ------------------------------------------------------------------
+ *
+ * Change a channel mode.
+ *
+ * Arguments:
+ *   - channel, the channel,
+ *   - mode, the mode.
+ */
+duk::Ret cmode(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->cmode(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.cnotice(channel, message)
+ * ------------------------------------------------------------------
+ *
+ * Send a channel notice.
+ *
+ * Arguments:
+ *   - channel, the channel,
+ *   - message, the message.
+ */
+duk::Ret cnotice(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->cnotice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.info()
+ * ------------------------------------------------------------------
+ *
+ * Get the server information as an object containing the following properties:
+ *
+ * name: the server unique name
+ * host: the host name
+ * port: the port number
+ * ssl: true if using ssl
+ * sslVerify: true if ssl was verified
+ * channels: an array of all channels
+ */
+duk::Ret info(duk::ContextPtr ctx)
+{
+	auto server = duk::self<duk::Shared<Server>>(ctx);
+
+	duk::push(ctx, duk::Object{});
+	duk::putProperty(ctx, -1, "name", server->info().name);
+	duk::putProperty(ctx, -1, "host", server->info().host);
+	duk::putProperty<int>(ctx, -1, "port", server->info().port);
+	duk::putProperty<bool>(ctx, -1, "ssl", server->info().flags & ServerInfo::Ssl);
+	duk::putProperty<bool>(ctx, -1, "sslVerify", server->info().flags & ServerInfo::SslVerify);
+	duk::putProperty(ctx, -1, "commandChar", server->settings().command);
+	duk::putProperty(ctx, -1, "realname", server->identity().realname);
+	duk::putProperty(ctx, -1, "nickname", server->identity().nickname);
+	duk::putProperty(ctx, -1, "username", server->identity().username);
+
+	/* Channels */
+	duk::push(ctx, duk::Array{});
+
+	int i = 0;
+	for (const auto &channel : server->settings().channels)
+		duk::putProperty(ctx, -1, i++, channel.name);
+
+	duk::putProperty(ctx, -2, "channels");
+
+	return 1;
+}
+
+/*
+ * Method: Server.invite(target, channel)
+ * ------------------------------------------------------------------
+ *
+ * Invite someone to a channel.
+ *
+ * Arguments:
+ *   - target, the target to invite,
+ *   - channel, the channel.
+ */
+duk::Ret invite(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->invite(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.join(channel, password = undefined)
+ * ------------------------------------------------------------------
+ *
+ * Join a channel with an optional password.
+ *
+ * Arguments:
+ *   - channel, the channel to join,
+ *   - password, the password or undefined to not use.
+ */
+duk::Ret join(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->join(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, ""));
+
+	return 0;
+}
+
+/*
+ * Method: Server.kick(target, channel, reason = undefined)
+ * ------------------------------------------------------------------
+ *
+ * Kick someone from a channel.
+ *
+ * Arguments:
+ *   - target, the target to kick,
+ *   - channel, the channel,
+ *   - reason, the optional reason or undefined to not set.
+ */
+duk::Ret kick(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->kick(
+		duk::require<std::string>(ctx, 0),
+		duk::require<std::string>(ctx, 1),
+		duk::optional<std::string>(ctx, 2, "")
+	);
+
+	return 0;
+}
+
+/*
+ * Method: Server.me(target, message)
+ * ------------------------------------------------------------------
+ *
+ * Send a CTCP Action.
+ *
+ * Arguments:
+ *   - target, the target or a channel,
+ *   - message, the message.
+ */
+duk::Ret me(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->me(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.message(target, message)
+ * ------------------------------------------------------------------
+ *
+ * Send a message.
+ *
+ * Arguments:
+ *   - target, the target or a channel,
+ *   - message, the message.
+ */
+duk::Ret message(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->message(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.mode(mode)
+ * ------------------------------------------------------------------
+ *
+ * Change your mode.
+ *
+ * Arguments:
+ *   - mode, the new mode.
+ */
+duk::Ret mode(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->mode(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Method: Server.names(channel)
+ * ------------------------------------------------------------------
+ *
+ * Get the list of names from a channel.
+ *
+ * Arguments:
+ *   - channel, the channel.
+ */
+duk::Ret names(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->names(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Method: Server.nick(nickname)
+ * ------------------------------------------------------------------
+ *
+ * Change the nickname.
+ *
+ * Arguments:
+ *   - nickname, the nickname.
+ */
+duk::Ret nick(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->nick(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Method: Server.notice(target, message)
+ * ------------------------------------------------------------------
+ *
+ * Send a private notice.
+ *
+ * Arguments:
+ *   - target, the target,
+ *   - message, the notice message.
+ */
+duk::Ret notice(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->notice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.part(channel, reason = undefined)
+ * ------------------------------------------------------------------
+ *
+ * Leave a channel.
+ *
+ * Arguments:
+ *   - channel, the channel to leave,
+ *   - reason, the optional reason, keep undefined for portability.
+ */
+duk::Ret part(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->part(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, ""));
+
+	return 0;
+}
+
+/*
+ * Method: Server.send(raw)
+ * ------------------------------------------------------------------
+ *
+ * Send a raw message to the IRC server.
+ *
+ * Arguments:
+ *   - raw, the raw message (without terminators).
+ */
+duk::Ret send(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->send(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Method: Server.topic(channel, topic)
+ * ------------------------------------------------------------------
+ *
+ * Change a channel topic.
+ *
+ * Arguments:
+ *   - channel, the channel,
+ *   - topic, the new topic.
+ */
+duk::Ret topic(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->topic(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1));
+
+	return 0;
+}
+
+/*
+ * Method: Server.whois(target)
+ * ------------------------------------------------------------------
+ *
+ * Get whois information.
+ *
+ * Arguments:
+ *   - target, the target.
+ */
+duk::Ret whois(duk::ContextPtr ctx)
+{
+	duk::self<duk::Shared<Server>>(ctx)->whois(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Method: Server.toString()
+ * ------------------------------------------------------------------
+ *
+ * Convert the object to std::string, convenience for adding the object
+ * as property key.
+ *
+ * Returns:
+ *   The server name (unique).
+ */
+duk::Ret toString(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::self<duk::Shared<Server>>(ctx)->info().name);
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Server(params) [constructor]
+ * ------------------------------------------------------------------
+ *
+ * Construct a new server.
+ *
+ * Params must be filled with the following properties:
+ *
+ * name: the name,
+ * host: the host,
+ * ipv6: true to use ipv6,	(Optional: default false)
+ * port: the port number,	(Optional: default 6667)
+ * password: the password,	(Optional: default none)
+ * channels: array of channels	(Optiona: default empty)
+ * ssl: true to use ssl,	(Optional: default false)
+ * sslVerify: true to verify	(Optional: default true)
+ * nickname: "nickname",	(Optional, default: irccd)
+ * username: "user name",	(Optional, default: irccd)
+ * realname: "real name",	(Optional, default: IRC Client Daemon)
+ * commandChar: "!",		(Optional, the command char, default: "!")
+ */
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	if (!duk_is_constructor_call(ctx))
+		return 0;
+
+	ServerInfo info;
+	ServerIdentity identity;
+	ServerSettings settings;
+
+	/* Information part */
+	info.name = duk::getProperty<std::string>(ctx, 0, "name");
+	info.host = duk::getProperty<std::string>(ctx, 0, "host");
+	info.port = duk::optionalProperty<int>(ctx, 0, "port", (int)info.port);
+	info.password = duk::optionalProperty<std::string>(ctx, 0, "password", "");
+
+	if (duk::optionalProperty<bool>(ctx, 0, "ipv6", false))
+		info.flags |= ServerInfo::Ipv6;
+
+	/* Identity part */
+	identity.nickname = duk::optionalProperty<std::string>(ctx, 0, "nickname", identity.nickname);
+	identity.username = duk::optionalProperty<std::string>(ctx, 0, "username", identity.username);
+	identity.realname = duk::optionalProperty<std::string>(ctx, 0, "realname", identity.realname);
+	identity.ctcpversion = duk::optionalProperty<std::string>(ctx, 0, "version", identity.ctcpversion);
+
+	/* Settings part */
+	for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels"))
+		settings.channels.push_back(Server::splitChannel(chan));
+
+	settings.recotries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.recotries);
+	settings.recotimeout = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.recotimeout);
+
+	if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false))
+		settings.flags |= ServerSettings::JoinInvite;
+	if (duk::optionalProperty<bool>(ctx, 0, "autoRejoin", false))
+		settings.flags |= ServerSettings::AutoRejoin;
+
+	try {
+		duk::construct(ctx, duk::Shared<Server>{std::make_shared<Server>(std::move(info), std::move(identity), std::move(settings))});
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, duk::Error(ex.what()));
+	}
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.Server.add(s)
+ * ------------------------------------------------------------------
+ *
+ * Register a new server to the irccd instance.
+ *
+ * Arguments:
+ *   - s, the server to add.
+ */
+duk::Ret add(duk::ContextPtr ctx)
+{
+	auto server = duk::get<duk::Shared<Server>>(ctx, 0);
+
+	if (server)
+		duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->addServer(server);
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.Server.find(name)
+ * ------------------------------------------------------------------
+ *
+ * Find a server by name.
+ *
+ * Arguments:
+ *   - name, the server name
+ * Returns:
+ *   The server object or undefined if not found.
+ */
+duk::Ret find(duk::ContextPtr ctx)
+{
+	const auto name = duk::require<std::string>(ctx, 0);
+	const auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd");
+
+	try {
+		duk::push(ctx, duk::Shared<Server>{irccd->requireServer(name)});
+	} catch (...) {
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Server.list()
+ * ------------------------------------------------------------------
+ *
+ * Get the map of all loaded servers.
+ *
+ * Returns:
+ *   An object with string-to-servers pairs.
+ */
+duk::Ret list(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::Object{});
+
+	for (const auto &pair : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->servers())
+		duk::putProperty(ctx, -1, pair.first, duk::Shared<Server>{pair.second});
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Server.remove(name)
+ * ------------------------------------------------------------------
+ *
+ * Remove a server from the irccd instance. You can pass the server object since it's coercible to a string.
+ *
+ * Arguments:
+ *   - name the server name.
+ */
+duk::Ret remove(duk::ContextPtr ctx)
+{
+	duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->removeServer(duk::require<std::string>(ctx, 0));
+
+	return 0;
+}
+
+const duk::FunctionMap methods{
+	{ "cmode",	{ cmode,	2		} },
+	{ "cnotice",	{ cnotice,	2		} },
+	{ "info",	{ info,		0		} },
+	{ "invite",	{ invite,	2		} },
+	{ "join",	{ join,		DUK_VARARGS	} },
+	{ "kick",	{ kick,		DUK_VARARGS	} },
+	{ "me",		{ me,		2		} },
+	{ "message",	{ message,	2		} },
+	{ "mode",	{ mode,		1		} },
+	{ "names",	{ names,	1		} },
+	{ "nick",	{ nick,		1		} },
+	{ "notice",	{ notice,	2		} },
+	{ "part",	{ part,		DUK_VARARGS	} },
+	{ "send",	{ send,		1		} },
+	{ "topic",	{ topic,	2		} },
+	{ "whois",	{ whois,	1		} },
+	{ "toString",	{ toString,	0		} }
+};
+
+const duk::FunctionMap functions{
+	{ "add",	{ add,		1		} },
+	{ "find",	{ find,		1		} },
+	{ "list",	{ list,		0		} },
+	{ "remove",	{ remove,	1		} }
+};
+
+} // !namespace
+
+void loadJsServer(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Function{constructor, 1});
+	duk::push(ctx, functions);
+	duk::push(ctx, duk::Object());
+	duk::push(ctx, methods);
+	duk::putProperty(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "Server");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-server.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,59 @@
+/*
+ * js-server.h -- Irccd.Server API
+ *
+ * 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_JS_SERVER_H
+#define IRCCD_JS_SERVER_H
+
+#include <irccd/server.h>
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsServer(duk::ContextPtr ctx);
+
+namespace duk {
+
+template <>
+class TypeTraits<irccd::Server> {
+public:
+	static inline void prototype(ContextPtr ctx)
+	{
+		getGlobal<void>(ctx, "Irccd");
+		getGlobal<void>(ctx, "Server");
+		getProperty<void>(ctx, -1, "prototype");
+		remove(ctx, -2);
+		remove(ctx, -2);
+	}
+
+	static inline std::string name()
+	{
+		return "\xff""\xff""Server";
+	}
+
+	static inline std::vector<std::string> inherits()
+	{
+		return {};
+	}
+};
+
+} // !duk
+
+} // !irccd
+
+#endif // !IRCCD_JS_SERVER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-system.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,237 @@
+/*
+ * js-system.cpp -- Irccd.System API
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd-config.h>
+
+#include <chrono>
+
+#if defined(HAVE_POPEN)
+#  include <cstdio>
+#endif
+
+#include <cstdlib>
+#include <thread>
+
+#include "js-file.h"
+#include "js-irccd.h"
+#include "js-system.h"
+#include "system.h"
+
+namespace irccd {
+
+namespace {
+
+/*
+ * Function: Irccd.System.env(key)
+ * ------------------------------------------------------------------
+ *
+ * Get an environment system variable.
+ *
+ * Arguments:
+ *   - key, the environment variable.
+ * Returns:
+ *   The value.
+ */
+int env(duk::ContextPtr ctx)
+{
+	duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.System.exec(cmd)
+ * ------------------------------------------------------------------
+ *
+ * Execute a system command.
+ *
+ * Arguments:
+ *   - cmd, the command to execute.
+ */
+int exec(duk::ContextPtr ctx)
+{
+	std::system(duk::get<const char *>(ctx, 0));
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.System.home()
+ * ------------------------------------------------------------------
+ *
+ * Get the operating system user's home.
+ *
+ * Returns:
+ *   The user home directory.
+ */
+int home(duk::ContextPtr ctx)
+{
+	duk::push(ctx, sys::home());
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.System.name()
+ * ------------------------------------------------------------------
+ *
+ * Get the operating system name.
+ *
+ * Returns:
+ *   The system name.
+ */
+int name(duk::ContextPtr ctx)
+{
+	duk::push(ctx, sys::name());
+
+	return 1;
+}
+
+#if defined(HAVE_POPEN)
+
+/*
+ * Function: Irccd.System.popen(cmd, mode) [optional]
+ * ------------------------------------------------------------------
+ *
+ * Wrapper for popen(3) if the function is available.
+ *
+ * Arguments:
+ *   - cmd, the command to execute,
+ *   - mode, the mode (e.g. "r").
+ * Returns:
+ *   A Irccd.File object.
+ * Throws
+ *   - Irccd.SystemError on failures.
+ */
+int popen(duk::ContextPtr ctx)
+{
+	auto fp = ::popen(duk::require<const char *>(ctx, 0), duk::require<const char *>(ctx, 1));
+
+	if (fp == nullptr)
+		duk::raise(ctx, SystemError{});
+
+	duk::push(ctx, duk::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })});
+
+	return 1;
+}
+
+#endif // !HAVE_POPEN
+
+/*
+ * Function: Irccd.System.sleep(delay)
+ * ------------------------------------------------------------------
+ *
+ * Sleep the main loop for the specific delay in seconds.
+ */
+int sleep(duk::ContextPtr ctx)
+{
+	std::this_thread::sleep_for(std::chrono::seconds(duk::get<int>(ctx, 0)));
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.System.ticks()
+ * ------------------------------------------------------------------
+ *
+ * Get the number of milliseconds since irccd was started.
+ *
+ * Returns:
+ *   The number of milliseconds.
+ */
+int ticks(duk::ContextPtr ctx)
+{
+	duk::push(ctx, static_cast<int>(sys::ticks()));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.System.usleep(delay)
+ * ------------------------------------------------------------------
+ *
+ * Sleep the main loop for the specific delay in microseconds.
+ */
+int usleep(duk::ContextPtr ctx)
+{
+	std::this_thread::sleep_for(std::chrono::microseconds(duk::get<int>(ctx, 0)));
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.System.uptime()
+ * ------------------------------------------------------------------
+ *
+ * Get the system uptime.
+ *
+ * Returns:
+ *   The system uptime.
+ */
+int uptime(duk::ContextPtr ctx)
+{
+	duk::push<int>(ctx, sys::uptime());
+
+	return 0;
+}
+
+/*
+ * Function: Irccd.System.version()
+ * ------------------------------------------------------------------
+ *
+ * Get the operating system version.
+ *
+ * Returns:
+ *   The system version.
+ */
+int version(duk::ContextPtr ctx)
+{
+	duk::push(ctx, sys::version());
+
+	return 1;
+}
+
+const duk::FunctionMap functions{
+	{ "env",	{ env,		1	} },
+	{ "exec",	{ exec,		1	} },
+	{ "home",	{ home,		0	} },
+	{ "name",	{ name,		0	} },
+#if defined(HAVE_POPEN)
+	{ "popen",	{ popen,	2	} }, 
+#endif
+	{ "sleep",	{ sleep,	1	} },
+	{ "ticks",	{ ticks,	0	} },
+	{ "uptime",	{ uptime,	0	} },
+	{ "usleep",	{ usleep,	1	} },
+	{ "version",	{ version,	0	} }
+};
+
+} // !namespace
+
+void loadJsSystem(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, functions);
+	duk::putProperty(ctx, -2, "System");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-system.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * js-system.h -- Irccd.System API
+ *
+ * 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_JS_SYSTEM_H
+#define IRCCD_JS_SYSTEM_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsSystem(duk::ContextPtr ctx);
+
+} // !irccd
+
+#endif // !IRCCD_JS_SYSTEM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-timer.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,140 @@
+/*
+ * js-timer.cpp -- Irccd.Timer API
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cassert>
+#include <cstdint>
+
+#include "js.h"
+#include "plugin.h"
+
+namespace irccd {
+
+namespace duk {
+
+template <>
+class TypeTraits<Timer> {
+public:
+	static std::string name()
+	{
+		return "\xff""\xff""Timer";
+	}
+
+	static std::vector<std::string> inherits()
+	{
+		return {};
+	}
+};
+
+} // !duk
+
+namespace {
+
+/*
+ * Method: Timer.start()
+ * --------------------------------------------------------
+ *
+ * Start the timer. If the timer is already started the method
+ * is a no-op.
+ */
+duk::Ret start(duk::ContextPtr ctx)
+{
+	auto timer = duk::self<duk::Shared<Timer>>(ctx);
+
+	if (!timer->isRunning())
+		timer->start();
+
+	return 0;
+}
+
+/*
+ * Method: Timer.stop()
+ * --------------------------------------------------------
+ *
+ * Stop the timer.
+ */
+duk::Ret stop(duk::ContextPtr ctx)
+{
+	auto timer = duk::self<duk::Shared<Timer>>(ctx);
+
+	if (timer->isRunning())
+		timer->stop();
+
+	return 0;
+}
+
+const duk::FunctionMap methods{
+	{ "start",	{ start,	0 } },
+	{ "stop",	{ stop,		0 } }
+};
+
+/*
+ * Function: Irccd.Timer(type, delay, callback) [constructor]
+ * --------------------------------------------------------
+ *
+ * Create a new timer object.
+ *
+ * Arguments:
+ *   - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat),
+ *   - delay, the interval in milliseconds,
+ *   - callback, the function to call.
+ */
+duk::Ret constructor(duk::ContextPtr ctx)
+{
+	int type = duk::require<int>(ctx, 0);
+	int delay = duk::require<int>(ctx, 1);
+
+	if (!duk::is<duk::Function>(ctx, 2))
+		duk::raise(ctx, duk::TypeError("missing callback function"));
+
+	auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay);
+
+	/* Add this timer to the underlying plugin */
+	duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin")->addTimer(timer);
+
+	/* Construct object */
+	duk::construct(ctx, duk::Shared<Timer>{timer});
+
+	/* Now store the JavaScript function to call */
+	duk::dup(ctx, 2);
+	duk::putGlobal(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
+
+	return 0;
+}
+
+const duk::Map<int> constants{
+	{ "Single",	static_cast<int>(TimerType::Single) },
+	{ "Repeat",	static_cast<int>(TimerType::Repeat) },
+};
+
+} // !namespace
+
+void loadJsTimer(duk::ContextPtr ctx) noexcept
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Function{constructor, 3});
+	duk::push(ctx, constants);
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, methods);
+	duk::putProperty(ctx, -2, "prototype");
+	duk::putProperty(ctx, -2, "Timer");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-timer.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,31 @@
+/*
+ * js-timer.h -- Irccd.Timer API
+ *
+ * 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_JS_TIMER_H
+#define IRCCD_JS_TIMER_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsTimer(duk::ContextPtr ctx) noexcept;
+
+} // !irccd
+
+#endif // !IRCCD_JS_TIMER_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-unicode.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,144 @@
+/*
+ * js-unicode.cpp -- Irccd.Unicode API
+ *
+ * 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 "js.h"
+#include "unicode.h"
+
+namespace irccd {
+
+namespace {
+
+/*
+ * Function: Irccd.Unicode.isDigit(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is in the digit category.
+ */
+duk::Ret isDigit(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::isdigit(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Unicode.isLetter(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is in the letter category.
+ */
+duk::Ret isLetter(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::isalpha(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Unicode.isLower(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is lower case.
+ */
+duk::Ret isLower(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::islower(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Unicode.isSpace(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is in the space category.
+ */
+duk::Ret isSpace(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::isspace(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Unicode.isTitle(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is title case.
+ */
+duk::Ret isTitle(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::istitle(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Unicode.isUpper(code)
+ * --------------------------------------------------------
+ *
+ * Arguments:
+ *   - code, the code point.
+ * Returns:
+ *   True if the code is upper case.
+ */
+duk::Ret isUpper(duk::ContextPtr ctx)
+{
+	duk::push(ctx, unicode::isupper(duk::get<int>(ctx, 0)));
+
+	return 1;
+}
+
+const duk::FunctionMap functions{
+	{ "isDigit",		{ isDigit,	1	} },
+	{ "isLetter",		{ isLetter,	1	} },
+	{ "isLower",		{ isLower,	1	} },
+	{ "isSpace",		{ isSpace,	1	} },
+	{ "isTitle",		{ isTitle,	1	} },
+	{ "isUpper",		{ isUpper,	1	} },
+};
+
+} // !namespace
+
+void loadJsUnicode(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, functions);
+	duk::putProperty(ctx, -2, "Unicode");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-unicode.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * js-unicode.cpp -- Irccd.Unicode API
+ *
+ * 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_JS_UNICODE_H
+#define IRCCD_JS_UNICODE_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsUnicode(duk::ContextPtr ctx);
+
+} // !irccd
+
+#endif // !IRCCD_JS_UNICODE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-util.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,150 @@
+/*
+ * js-util.cpp -- Irccd.Util API
+ *
+ * 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 <libircclient.h>
+
+#include "js-util.h"
+#include "util.h"
+
+namespace irccd {
+
+namespace duk {
+
+/**
+ * Read parameters for Irccd.Util.format function, the object is defined as follow:
+ *
+ * {
+ *   date: the date object
+ *   flags: the flags (not implemented yet)
+ *   field1: a field to substitute in #{} pattern
+ *   field2: a field to substitute in #{} pattern
+ *   fieldn: ...
+ * }
+ */
+template <>
+class TypeTraits<util::Substitution> {
+public:
+	static util::Substitution get(ContextPtr ctx, int index)
+	{
+		util::Substitution params;
+
+		if (!duk::is<Object>(ctx, index))
+			return params;
+
+		duk::enumerate(ctx, index, 0, true, [&] (ContextPtr) {
+			if (duk::get<std::string>(ctx, -2) == "date")
+				params.time = static_cast<time_t>(duk::get<double>(ctx, -1) / 1000);
+			else
+				params.keywords.insert({duk::get<std::string>(ctx, -2), duk::get<std::string>(ctx, -1)});
+		});
+
+		return params;
+	}
+};
+
+} // !duk
+
+namespace {
+
+/*
+ * Function: Irccd.Util.format(text, parameters)
+ * --------------------------------------------------------
+ *
+ * Format a string with templates.
+ *
+ * Arguments:
+ *   - input, the text to update,
+ *   - params, the parameters.
+ * Returns:
+ *   The converted text.
+ */
+duk::Ret format(duk::ContextPtr ctx)
+{
+	try {
+		duk::push(ctx, util::format(duk::get<std::string>(ctx, 0), duk::get<util::Substitution>(ctx, 1)));
+	} catch (const std::exception &ex) {
+		duk::raise(ctx, duk::SyntaxError(ex.what()));
+	}
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Util.splituser(ident)
+ * --------------------------------------------------------
+ *
+ * Return the nickname part from a full username.
+ *
+ * Arguments:
+ *   - ident, the full identity.
+ * Returns:
+ *   The nickname.
+ */
+duk::Ret splituser(duk::ContextPtr ctx)
+{
+	const char *target = duk::require<const char *>(ctx, 0);
+	char nick[32] = {0};
+
+	irc_target_get_nick(target, nick, sizeof (nick) -1);
+	duk::push(ctx, std::string(nick));
+
+	return 1;
+}
+
+/*
+ * Function: Irccd.Util.splithost(ident)
+ * --------------------------------------------------------
+ *
+ * Return the hostname part from a full username.
+ *
+ * Arguments:
+ *   - ident, the full identity.
+ * Returns:
+ *   The hostname.
+ */
+duk::Ret splithost(duk::ContextPtr ctx)
+{
+	const char *target = duk::require<const char *>(ctx, 0);
+	char host[32] = {0};
+
+	irc_target_get_host(target, host, sizeof (host) -1);
+	duk::push(ctx, std::string(host));
+
+	return 1;
+}
+
+const duk::FunctionMap functions{
+	{ "format",		{ format,	DUK_VARARGS	} },
+	{ "splituser",		{ splituser,	1		} },
+	{ "splithost",		{ splithost,	1		} }
+};
+
+} // !namespace
+
+void loadJsUtil(duk::ContextPtr ctx)
+{
+	duk::StackAssert sa(ctx);
+
+	duk::getGlobal<void>(ctx, "Irccd");
+	duk::push(ctx, duk::Object{});
+	duk::push(ctx, functions);
+	duk::putProperty(ctx, -2, "Util");
+	duk::pop(ctx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js-util.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * js-util.h -- Irccd.Util API
+ *
+ * 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_JS_UTIL_H
+#define IRCCD_JS_UTIL_H
+
+#include "js.h"
+
+namespace irccd {
+
+void loadJsUtil(duk::ContextPtr ctx);
+
+} // !irccd
+
+#endif // !IRCCD_JS_UTIL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,2381 @@
+/*
+ * js.h -- JavaScript C++14 wrapper for Duktape
+ *
+ * Copyright (c) 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 JS_H
+#define JS_H
+
+/**
+ * @file js.h
+ * @brief Bring JavaScript using Duktape.
+ *
+ * This file provides usual Duktape function renamed and placed into `duk` namespace. It also replaces error
+ * code with exceptions when possible.
+ *
+ * For convenience, this file also provides templated functions, overloads and much more.
+ */
+
+#include <cassert>
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <duktape.h>
+
+namespace irccd {
+
+/**
+ * Duktape C++ namespace wrapper.
+ */
+namespace duk {
+
+class Context;
+
+using CodePoint = duk_codepoint_t;
+using ContextPtr = duk_context *;
+using Index = duk_idx_t;
+using Ret = duk_ret_t;
+using Size = duk_size_t;
+
+/**
+ * @class StackAssert
+ * @brief Stack sanity checker.
+ *
+ * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor
+ * will examinate if the stack size matches the user expected size.
+ *
+ * When compiled with NDEBUG, this class does nothing.
+ *
+ * To use it, just declare an lvalue at the beginning of your function.
+ */
+class StackAssert {
+#if !defined(NDEBUG)
+private:
+	ContextPtr m_context;
+	unsigned m_expected;
+	unsigned m_begin;
+#endif
+
+public:
+	/**
+	 * Create the stack checker.
+	 *
+	 * No-op if NDEBUG is set.
+	 *
+	 * @param ctx the context
+	 * @param expected the size expected relative to the already existing values
+	 */
+	inline StackAssert(ContextPtr ctx, unsigned expected = 0) noexcept
+#if !defined(NDEBUG)
+		: m_context(ctx)
+		, m_expected(expected)
+		, m_begin(static_cast<unsigned>(duk_get_top(ctx)))
+#endif
+	{
+#if defined(NDEBUG)
+		(void)ctx;
+		(void)expected;
+#endif
+	}
+
+	/**
+	 * Verify the expected size.
+	 *
+	 * No-op if NDEBUG is set.
+	 */
+	inline ~StackAssert() noexcept
+	{
+#if !defined(NDEBUG)
+		assert((unsigned)duk_get_top(m_context) - m_begin == m_expected);
+#endif
+	}
+};
+
+/**
+ * @class Object
+ * @brief Empty class tag for push() function.
+ */
+class Object {
+};
+
+/**
+ * @class Array
+ * @brief Empty class tag for push() function.
+ */
+class Array {
+};
+
+/**
+ * @class Global
+ * @brief Empty class tag to push the global object.
+ */
+class Global {
+};
+
+/**
+ * @class Undefined
+ * @brief Empty class tag to push undefined to the stack.
+ */
+class Undefined {
+};
+
+/**
+ * @class Null
+ * @brief Empty class tag to push null to the stack.
+ */
+class Null {
+};
+
+/**
+ * @class This
+ * @brief Empty class tag to push this binding to the stack.
+ */
+class This {
+};
+
+/**
+ * @class RawPointer
+ * @brief Push a non-managed pointer to Duktape, the pointer will never be deleted.
+ * @note For a managed pointer with prototype, see Pointer
+ */
+template <typename T>
+class RawPointer {
+public:
+	/**
+	 * The pointer to push.
+	 */
+	T *object;
+};
+
+/**
+ * @brief Manage shared_ptr from C++ and JavaScript
+ *
+ * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership
+ * and deletion.
+ *
+ */
+template <typename T>
+class Shared {
+public:
+	/**
+	 * The shared object.
+	 */
+	std::shared_ptr<T> object;
+};
+
+/**
+ * @brief Manage pointers from C++ and JavaScript
+ *
+ * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when
+ * the JavaScript garbage collectors collect them so never store a pointer created with this.
+ *
+ * The only requirement is to have the function `void prototype(Context &ctx)` in your class T.
+ */
+template <typename T>
+class Pointer {
+public:
+	/**
+	 * The object.
+	 */
+	T *object{nullptr};
+};
+
+/**
+ * @class Function
+ * @brief Duktape/C function definition.
+ *
+ * This class wraps the std::function as a Duktape/C function by storing a copied pointer.
+ */
+class Function {
+public:
+	/**
+	 * The function pointer, must not be null.
+	 */
+	duk_c_function function;
+
+	/**
+	 * Number of args that the function takes
+	 */
+	duk_idx_t nargs{0};
+};
+
+/**
+ * Map of functions to set on an object.
+ */
+using FunctionMap = std::unordered_map<std::string, Function>;
+
+/**
+ * Map of string to type, ideal for setting constants like enums.
+ */
+template <typename Type>
+using Map = std::unordered_map<std::string, Type>;
+
+/**
+ * @class ErrorInfo
+ * @brief Error description.
+ *
+ * This class fills the fields got in an Error object.
+ */
+class ErrorInfo : public std::exception {
+public:
+	std::string name;		//!< name of error
+	std::string message;		//!< error message
+	std::string stack;		//!< stack if available
+	std::string fileName;		//!< filename if applicable
+	int lineNumber{0};		//!< line number if applicable
+
+	/**
+	 * Get the error message. This effectively returns message field.
+	 *
+	 * @return the message
+	 */
+	const char *what() const noexcept override
+	{
+		return message.c_str();
+	}
+};
+
+/**
+ * @class TypeTraits
+ * @brief Type information to implement new types in JavaScript's context.
+ *
+ * This class depending on your needs may have the following functions:
+ *
+ * - `static void construct(Context &ctx, Type value)`
+ * - `static Type get(Context &ctx, int index)`
+ * - `static bool is(Context &ctx, int index)`
+ * - `static Type optional(Context &ctx, int index, Type defaultValue)`
+ * - `static void push(Context &ctx, Type value)`
+ * - `static Type require(Context &ctx, int index)`
+ *
+ * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors).
+ *
+ * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the
+ * stack.
+ *
+ * The `is` function is used in Context::is to check if the value on the stack is of type `Type`.
+ *
+ * The `optional` function is used in Context::optional to get a value or a replacement if not applicable.
+ *
+ * The `push` function is used in Context::push to usually create a new value on the stack but some specializations
+ * may not (e.g. FunctionMap).
+ *
+ * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if
+ * not applicable.
+ *
+ * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`.
+ *
+ * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`.
+ */
+template <typename Type>
+class TypeTraits {
+};
+
+/**
+ * @class Context
+ * @brief RAII based Duktape handler.
+ *
+ * This class is implicitly convertible to duk_context for convenience.
+ */
+class Context {
+private:
+	using Deleter = void (*)(duk_context *);
+	using Handle = std::unique_ptr<duk_context, Deleter>;
+
+	Handle m_handle;
+
+	Context(const Context &) = delete;
+	Context &operator=(const Context &) = delete;
+	Context(const Context &&) = delete;
+	Context &operator=(const Context &&) = delete;
+
+public:
+	/**
+	 * Create default context.
+	 */
+	inline Context()
+		: m_handle(duk_create_heap_default(), duk_destroy_heap)
+	{
+	}
+
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * @return the duk_context
+	 */
+	inline operator duk_context *() noexcept
+	{
+		return m_handle.get();
+	}
+
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * @return the duk_context
+	 */
+	inline operator duk_context *() const noexcept
+	{
+		return m_handle.get();
+	}
+};
+
+/**
+ * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
+ *
+ * @param ctx the context
+ * @param index the index
+ * @return the information
+ */
+inline ErrorInfo error(ContextPtr ctx, int index)
+{
+	ErrorInfo error;
+
+	index = duk_normalize_index(ctx, index);
+
+	duk_get_prop_string(ctx, index, "name");
+	error.name = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "message");
+	error.message = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "fileName");
+	error.fileName = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "lineNumber");
+	error.lineNumber = duk_to_int(ctx, -1);
+	duk_get_prop_string(ctx, index, "stack");
+	error.stack = duk_to_string(ctx, -1);
+	duk_pop_n(ctx, 5);
+
+	return error;
+}
+
+/**
+ * Wrapper for [duk_base64_decode](http://duktape.org/api.html#duk_base64_decode).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline void base64Decode(ContextPtr ctx, Index index)
+{
+	duk_base64_decode(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_base64_encode](http://duktape.org/api.html#duk_base64_encode).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline void base64Encode(ContextPtr ctx, Index index)
+{
+	duk_base64_encode(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_call](http://duktape.org/api.html#duk_call).
+ *
+ * @param ctx the context
+ * @param nargs the number of arguments
+ */
+inline void call(ContextPtr ctx, Index nargs = 0)
+{
+	duk_call(ctx, nargs);
+}
+
+/**
+ * Wrapper for [duk_call_method](http://duktape.org/api.html#duk_call_method).
+ *
+ * @param ctx the context
+ * @param nargs the number of arguments
+ */
+inline void callMethod(ContextPtr ctx, Index nargs = 0)
+{
+	duk_call_method(ctx, nargs);
+}
+
+/**
+ * Wrapper for [duk_call_prop](http://duktape.org/api.html#duk_call_prop).
+ *
+ * @param ctx the context
+ * @param index the object index
+ * @param nargs the number of arguments
+ */
+inline void callProperty(ContextPtr ctx, Index index, Index nargs = 0)
+{
+	duk_call_prop(ctx, index, nargs);
+}
+
+/**
+ * Wrapper for [duk_char_code_at](http://duktape.org/api.html#duk_char_code_at).
+ *
+ * @param ctx the context
+ * @param index the index
+ * @param charOffset the offset
+ */
+inline CodePoint charCodeAt(ContextPtr ctx, Index index, Size charOffset)
+{
+	return duk_char_code_at(ctx, index, charOffset);
+}
+
+/**
+ * Wrapper for [duk_check_stack](http://duktape.org/api.html#duk_check_stack).
+ *
+ * @param ctx the context
+ * @param extra the extra space
+ * @return true if space is available
+ */
+inline bool checkStack(ContextPtr ctx, Index extra)
+{
+	return duk_check_stack(ctx, extra);
+}
+
+/**
+ * Wrapper for [duk_check_stack_top](http://duktape.org/api.html#duk_check_stack_top).
+ *
+ * @param ctx the context
+ * @param top the extra space
+ * @return true if space is available
+ */
+inline bool checkStackTop(ContextPtr ctx, Index top)
+{
+	return duk_check_stack_top(ctx, top);
+}
+
+/**
+ * Wrapper for [duk_check_type](http://duktape.org/api.html#duk_check_type).
+ *
+ * @param ctx the context
+ * @param index the value index
+ * @param type the desired type
+ * @return true if object is given type
+ */
+inline bool checkType(ContextPtr ctx, Index index, int type)
+{
+	return duk_check_type(ctx, index, type);
+}
+
+/**
+ * Wrapper for [duk_check_type_mask](http://duktape.org/api.html#duk_check_type_mask).
+ *
+ * @param ctx the context
+ * @param index the value index
+ * @param mask the desired mask
+ * @return true if object is one of the type
+ */
+inline bool checkTypeMask(ContextPtr ctx, Index index, unsigned mask)
+{
+	return duk_check_type_mask(ctx, index, mask);
+}
+
+/**
+ * Wrapper for [duk_compact](http://duktape.org/api.html#duk_compact).
+ *
+ * @param ctx the context
+ * @param objIndex the object index
+ */
+inline void compact(ContextPtr ctx, Index objIndex)
+{
+	duk_compact(ctx, objIndex);
+}
+
+/**
+ * Wrapper for [duk_concat](http://duktape.org/api.html#duk_concat).
+ *
+ * @param ctx the context
+ * @param count the number of values
+ */
+inline void concat(ContextPtr ctx, Index count)
+{
+	duk_concat(ctx, count);
+}
+
+/**
+ * Wrapper for [duk_copy](http://duktape.org/api.html#duk_copy).
+ *
+ * @param from the from index
+ * @param to the destination
+ */
+inline void copy(ContextPtr ctx, Index from, Index to)
+{
+	duk_copy(ctx, from, to);
+}
+
+/**
+ * Wrapper for [duk_def_prop](http://duktape.org/api.html#duk_def_prop).
+ *
+ * @param index the object index
+ * @param flags the flags
+ */
+inline void defineProperty(ContextPtr ctx, Index index, unsigned flags)
+{
+	duk_def_prop(ctx, index, flags);
+}
+
+/**
+ * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop).
+ *
+ * @param index the object index
+ * @return true if deleted
+ */
+inline bool deleteProperty(ContextPtr ctx, Index index)
+{
+	return duk_del_prop(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop).
+ *
+ * @param index the object index
+ * @param position the property index
+ * @return true if deleted
+ */
+inline bool deleteProperty(ContextPtr ctx, Index index, unsigned position)
+{
+	return duk_del_prop_index(ctx, index, position);
+}
+
+/**
+ * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop).
+ *
+ * @param index the object index
+ * @param name the property name
+ * @return true if deleted
+ */
+inline bool deleteProperty(ContextPtr ctx, Index index, const std::string &name)
+{
+	return duk_del_prop_string(ctx, index, name.c_str());
+}
+
+/**
+ * Wrapper for [duk_dup](http://duktape.org/api.html#duk_dup).
+ *
+ * @param index the value to copy
+ */
+inline void dup(ContextPtr ctx, int index = -1)
+{
+	duk_dup(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_equals](http://duktape.org/api.html#duk_equals).
+ *
+ * @param ctx the context
+ * @param index1 the first value
+ * @param index2 the second value
+ * @return true if they equal
+ */
+inline bool equals(ContextPtr ctx, Index index1, Index index2)
+{
+	return duk_equals(ctx, index1, index2);
+}
+
+/**
+ * Wrapper for [duk_eval](http://duktape.org/api.html#duk_eval).
+ *
+ * @param ctx the context
+ */
+inline void eval(ContextPtr ctx)
+{
+	duk_eval(ctx);
+}
+
+/**
+ * Wrapper for [duk_eval_file](http://duktape.org/api.html#duk_eval_file).
+ *
+ * @param ctx the context
+ * @param path the path
+ * @param result true to get the result at the top of the stack
+ */
+inline void evalFile(ContextPtr ctx, const std::string &path, bool result = true)
+{
+	if (result)
+		duk_eval_file(ctx, path.c_str());
+	else
+		duk_eval_file_noresult(ctx, path.c_str());
+}
+
+/**
+ * Wrapper for [duk_eval_string](http://duktape.org/api.html#duk_eval_string).
+ *
+ * @param ctx the context
+ * @param src the source script
+ * @param result true to get the result at the top of the stack
+ */
+inline void evalString(ContextPtr ctx, const std::string &src, bool result = true)
+{
+	if (result)
+		duk_eval_string(ctx, src.c_str());
+	else
+		duk_eval_string_noresult(ctx, src.c_str());
+}
+/**
+ * Wrapper for [duk_gc](http://duktape.org/api.html#duk_gc).
+ *
+ * @param ctx the context
+ * @param flags the flags
+ */
+inline void gc(ContextPtr ctx, unsigned flags = 0)
+{
+	duk_gc(ctx, flags);
+}
+
+/**
+ * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop).
+ *
+ * @param ctx the context
+ * @param index the object index
+ * @return true if has
+ */
+inline bool hasProperty(ContextPtr ctx, Index index)
+{
+	return duk_has_prop(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop).
+ *
+ * @param ctx the context
+ * @param index the object index
+ * @param position the property index
+ * @return true if has
+ */
+inline bool hasProperty(ContextPtr ctx, Index index, unsigned position)
+{
+	return duk_has_prop_index(ctx, index, position);
+}
+
+/**
+ * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop).
+ *
+ * @param ctx the context
+ * @param index the object index
+ * @param name the property name
+ * @return true if has
+ */
+inline bool hasProperty(ContextPtr ctx, int index, const std::string &name)
+{
+	return duk_has_prop_string(ctx, index, name.c_str());
+}
+
+/**
+ * Wrapper for [duk_insert](http://duktape.org/api.html#duk_insert).
+ *
+ * @param ctx the context
+ * @param to the destination
+ * @note Wrapper of duk_insert
+ */
+inline void insert(ContextPtr ctx, Index to)
+{
+	duk_insert(ctx, to);
+}
+
+/**
+ * Wrapper for [duk_instanceof](http://duktape.org/api.html#duk_instanceof).
+ *
+ * @param ctx the context
+ * @param idx1 the value to test
+ * @param idx2 the instance requested
+ * @return true if idx1 is instance of idx2
+ */
+inline bool instanceof(ContextPtr ctx, Index idx1, Index idx2)
+{
+	return duk_instanceof(ctx, idx1, idx2);
+}
+
+/**
+ * Wrapper for [duk_join](http://duktape.org/api.html#duk_join).
+ *
+ * @param ctx the context
+ * @param count the number of values
+ */
+inline void join(ContextPtr ctx, Index count)
+{
+	duk_join(ctx, count);
+}
+
+/**
+ * Wrapper for [duk_json_decode](http://duktape.org/api.html#duk_json_decode).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline void jsonDecode(ContextPtr ctx, Index index)
+{
+	duk_json_decode(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_json_encode](http://duktape.org/api.html#duk_json_encode).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline void jsonEncode(ContextPtr ctx, Index index)
+{
+	duk_json_encode(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_normalize_index](http://duktape.org/api.html#duk_normalize_index).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline Index normalizeIndex(ContextPtr ctx, Index index)
+{
+	return duk_normalize_index(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_pcall](http://duktape.org/api.html#duk_pcall).
+ *
+ * @param ctx the context
+ * @param nargs the number of arguments
+ */
+inline int pcall(ContextPtr ctx, Index nargs = 0)
+{
+	return duk_pcall(ctx, nargs);
+}
+
+/**
+ * Wrapper for [duk_peval](http://duktape.org/api.html#duk_peval).
+ *
+ * @param ctx the context
+ */
+inline int peval(ContextPtr ctx)
+{
+	return duk_peval(ctx);
+}
+
+/**
+ * Wrapper for [duk_peval_file](http://duktape.org/api.html#duk_peval_file).
+ *
+ * @param ctx the context
+ * @param path the path
+ * @param result true to get the result at the top of the stack
+ */
+inline int pevalFile(ContextPtr ctx, const std::string &path, bool result = true)
+{
+	return result ? duk_peval_file(ctx, path.c_str()) : duk_peval_file_noresult(ctx, path.c_str());
+}
+
+/**
+ * Wrapper for [duk_peval_string](http://duktape.org/api.html#duk_peval_string).
+ *
+ * @param ctx the context
+ * @param src the source script
+ * @param result true to get the result at the top of the stack
+ */
+inline int pevalString(ContextPtr ctx, const std::string &src, bool result = true)
+{
+	return result ? duk_peval_string(ctx, src.c_str()) : duk_peval_string_noresult(ctx, src.c_str());
+}
+
+/**
+ * Wrapper for [duk_pop_n](http://duktape.org/api.html#duk_pop_n).
+ *
+ * @param ctx the context
+ * @param count the number of values to pop
+ */
+inline void pop(ContextPtr ctx, Index count = 1)
+{
+	duk_pop_n(ctx, count);
+}
+
+/**
+ * Wrapper for [duk_remove](http://duktape.org/api.html#duk_remove).
+ *
+ * @param ctx the context
+ * @param index the value to remove
+ */
+inline void remove(ContextPtr ctx, Index index)
+{
+	duk_remove(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_replace](http://duktape.org/api.html#duk_replace).
+ *
+ * @param ctx the context
+ * @param index the value to replace by the value at the top of the stack
+ */
+inline void replace(ContextPtr ctx, Index index)
+{
+	duk_replace(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_set_prototype](http://duktape.org/api.html#duk_set_prototype).
+ *
+ * @param ctx the context
+ * @param index the value index
+ */
+inline void setPrototype(ContextPtr ctx, Index index)
+{
+	duk_set_prototype(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_swap](http://duktape.org/api.html#duk_swap).
+ *
+ * @param ctx the context
+ * @param index1 the first index
+ * @param index2 the second index
+ */
+inline void swap(ContextPtr ctx, Index index1, Index index2)
+{
+	duk_swap(ctx, index1, index2);
+}
+
+/**
+ * Wrapper for [duk_swap_top](http://duktape.org/api.html#duk_swap_top).
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+inline void swapTop(ContextPtr ctx, Index index)
+{
+	duk_swap_top(ctx, index);
+}
+
+/**
+ * Wrapper for [duk_get_top](http://duktape.org/api.html#duk_get_top).
+ *
+ * @param ctx the context
+ * @return the stack size
+ */
+inline int top(ContextPtr ctx) noexcept
+{
+	return duk_get_top(ctx);
+}
+
+/**
+ * Wrapper for [duk_get_type](http://duktape.org/api.html#duk_get_type).
+ *
+ * @param ctx the context
+ * @param index the idnex
+ * @return the type
+ */
+inline int type(ContextPtr ctx, Index index) noexcept
+{
+	return duk_get_type(ctx, index);
+}
+
+/*
+ * Push / Get / Require / Is / Optional
+ * ----------------------------------------------------------
+ *
+ * The following functions are used to push, get or check values from the stack. They use specialization
+ * of TypeTraits class.
+ */
+
+/**
+ * Push a value into the stack. Calls TypeTraits<T>::push(*this, value);
+ *
+ * @param value the value to forward
+ */
+template <typename Type>
+inline void push(ContextPtr ctx, Type &&value)
+{
+	TypeTraits<std::decay_t<Type>>::push(ctx, std::forward<Type>(value));
+}
+
+/**
+ * Generic template function to get a value from the stack.
+ *
+ * @param index the index
+ * @return the value
+ */
+template <typename Type>
+inline auto get(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::get(ctx, 0))
+{
+	return TypeTraits<Type>::get(ctx, index);
+}
+
+/**
+ * Require a type at the specified index.
+ *
+ * @param index the index
+ * @return the value
+ */
+template <typename Type>
+inline auto require(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::require(ctx, 0))
+{
+	return TypeTraits<Type>::require(ctx, index);
+}
+
+/**
+ * Check if a value is a type of T.
+ *
+ * The TypeTraits<T> must have `static bool is(ContextPtr ptr, int index)`.
+ *
+ * @param index the value index
+ * @return true if is the type
+ */
+template <typename T>
+inline bool is(ContextPtr ctx, int index)
+{
+	return TypeTraits<T>::is(ctx, index);
+}
+
+/**
+ * Get an optional value from the stack, if the value is not available of not the correct type,
+ * return defaultValue instead.
+ *
+ * The TypeTraits<T> must have `static T optional(Context &, int index, T &&defaultValue)`.
+ *
+ * @param index the value index
+ * @param defaultValue the value replacement
+ * @return the value or defaultValue
+ */
+template <typename Type>
+inline auto optional(ContextPtr ctx, int index, Type &&defaultValue)
+{
+	return TypeTraits<std::decay_t<Type>>::optional(ctx, index, std::forward<Type>(defaultValue));
+}
+
+/*
+ * Properties management
+ * ----------------------------------------------------------
+ *
+ * The following functions are used to read or set properties on objects or globals also using TypeTraits.
+ */
+
+/**
+ * Get the property `name' as value from the object at the specified index.
+ *
+ * @param index the object index
+ * @param name the property name
+ * @return the value
+ * @note The stack is unchanged
+ */
+template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
+inline auto getProperty(ContextPtr ctx, int index, const std::string &name) -> decltype(get<Type>(ctx, 0))
+{
+	duk_get_prop_string(ctx, index, name.c_str());
+	decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1);
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Get a property by index, for arrays.
+ *
+ * @param index the object index
+ * @param position the position int the object
+ * @return the value
+ * @note The stack is unchanged
+ */
+template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
+inline auto getProperty(ContextPtr ctx, int index, int position) -> decltype(get<Type>(ctx, 0))
+{
+	duk_get_prop_index(ctx, index, position);
+	decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1);
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Get the property `name' and push it to the stack from the object at the specified index.
+ *
+ * @param index the object index
+ * @param name the property name
+ * @note The stack contains the property value
+ */
+template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
+inline void getProperty(ContextPtr ctx, int index, const std::string &name)
+{
+	duk_get_prop_string(ctx, index, name.c_str());
+}
+
+/**
+ * Get the property by index and push it to the stack from the object at the specified index.
+ *
+ * @param index the object index
+ * @param position the position in the object
+ * @note The stack contains the property value
+ */
+template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
+inline void getProperty(ContextPtr ctx, int index, int position)
+{
+	duk_get_prop_index(ctx, index, position);
+}
+
+/**
+ * Get an optional property `name` from the object at the specified index.
+ *
+ * @param index the object index
+ * @param name the property name
+ * @param def the default value
+ * @return the value or def
+ * @note The stack is unchanged
+ */
+template <typename Type, typename DefaultValue>
+inline auto optionalProperty(ContextPtr ctx, int index, const std::string &name, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def)))
+{
+	duk_get_prop_string(ctx, index, name.c_str());
+	decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def));
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Get an optional property by index, for arrays
+ *
+ * @param index the object index
+ * @param position the position int the object
+ * @param def the default value
+ * @return the value or def
+ * @note The stack is unchanged
+ */
+template <typename Type, typename DefaultValue>
+inline auto optionalProperty(ContextPtr ctx, int index, int position, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def)))
+{
+	duk_get_prop_index(ctx, index, position);
+	decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def));
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Set a property to the object at the specified index.
+ *
+ * @param index the object index
+ * @param name the property name
+ * @param value the value to forward
+ * @note The stack is unchanged
+ */
+template <typename Type>
+void putProperty(ContextPtr ctx, int index, const std::string &name, Type &&value)
+{
+	index = duk_normalize_index(ctx, index);
+
+	push(ctx, std::forward<Type>(value));
+	duk_put_prop_string(ctx, index, name.c_str());
+}
+
+/**
+ * Set a property by index, for arrays.
+ *
+ * @param index the object index
+ * @param position the position in the object
+ * @param value the value to forward
+ * @note The stack is unchanged
+ */
+template <typename Type>
+void putProperty(ContextPtr ctx, int index, int position, Type &&value)
+{
+	index = duk_normalize_index(ctx, index);
+
+	push(ctx, std::forward<Type>(value));
+	duk_put_prop_index(ctx, index, position);
+}
+
+/**
+ * Put the value that is at the top of the stack as property to the object.
+ *
+ * @param index the object index
+ * @param name the property name
+ */
+inline void putProperty(ContextPtr ctx, int index, const std::string &name)
+{
+	duk_put_prop_string(ctx, index, name.c_str());
+}
+
+/**
+ * Put the value that is at the top of the stack to the object as index.
+ *
+ * @param index the object index
+ * @param position the position in the object
+ */
+inline void putProperty(ContextPtr ctx, int index, int position)
+{
+	duk_put_prop_index(ctx, index, position);
+}
+
+/**
+ * Get a global value.
+ *
+ * @param name the name of the global variable
+ * @return the value
+ */
+template <typename Type>
+inline auto getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(ctx, 0))
+{
+	duk_get_global_string(ctx, name.c_str());
+	decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1);
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Overload that push the value at the top of the stack instead of returning it.
+ */
+template <typename Type>
+inline void getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept
+{
+	duk_get_global_string(ctx, name.c_str());
+}
+
+/**
+ * Set a global variable.
+ *
+ * @param name the name of the global variable
+ * @param type the value to set
+ */
+template <typename Type>
+inline void putGlobal(ContextPtr ctx, const std::string &name, Type&& type)
+{
+	push(ctx, std::forward<Type>(type));
+	duk_put_global_string(ctx, name.c_str());
+}
+
+/**
+ * Put the value at the top of the stack as global property.
+ *
+ * @param name the property name
+ */
+inline void putGlobal(ContextPtr ctx, const std::string &name)
+{
+	duk_put_global_string(ctx, name.c_str());
+}
+
+/*
+ * Extra functions
+ * ----------------------------------------------------------
+ *
+ * The following functions are implemented for convenience and do not exists in the native Duktape API.
+ */
+
+/**
+ * Enumerate an object or an array at the specified index.
+ *
+ * @param index the object or array index
+ * @param flags the optional flags to pass to duk_enum
+ * @param getvalue set to true if you want to extract the value
+ * @param func the function to call for each properties
+ */
+template <typename Func>
+void enumerate(ContextPtr ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func)
+{
+	duk_enum(ctx, index, flags);
+
+	while (duk_next(ctx, -1, getvalue)) {
+		func(ctx);
+		duk_pop_n(ctx, 1 + (getvalue ? 1 : 0));
+	}
+
+	duk_pop(ctx);
+}
+
+/**
+ * Return the this binding of the current function.
+ *
+ * @return the this binding as the template given
+ */
+template <typename T>
+inline auto self(ContextPtr ctx) -> decltype(TypeTraits<T>::get(ctx, 0))
+{
+	duk_push_this(ctx);
+	decltype(TypeTraits<T>::get(ctx, 0)) value = TypeTraits<T>::get(ctx, -1);
+	duk_pop(ctx);
+
+	return value;
+}
+
+/**
+ * Throw an ECMAScript exception.
+ *
+ * @param ex the exception
+ */
+template <typename Exception>
+void raise(ContextPtr ctx, const Exception &ex)
+{
+	ex.raise(ctx);
+}
+
+/**
+ * Wrapper for duk_throw.
+ *
+ * @param ctx the context
+ */
+inline void raise(ContextPtr ctx)
+{
+	duk_throw(ctx);
+}
+
+/**
+ * Wrapper for duk_error.
+ *
+ * @param ctx the context
+ * @param type the error type (e.g. DUK_ERR_REFERENCE_ERROR)
+ * @param fmt the format string
+ * @param args the arguments
+ */
+template <typename... Args>
+inline void raise(ContextPtr ctx, int type, const char *fmt, Args&&... args)
+{
+	duk_error(ctx, type, fmt, std::forward<Args>(args)...);
+}
+
+/**
+ * Wrapper for duk_new.
+ *
+ * @param ctx the context
+ * @param nargs the number of arguments
+ */
+inline void create(ContextPtr ctx, int nargs = 0)
+{
+	duk_new(ctx, nargs);
+}
+
+/**
+ * Construct the object in place, setting value as this binding.
+ *
+ * The TypeTraits<T> must have the following requirements:
+ *
+ * - static void construct(Context &, T): must update this with the value and keep the stack unchanged
+ *
+ * @param value the value to forward
+ * @see self
+ */
+template <typename T>
+inline void construct(ContextPtr ctx, T &&value)
+{
+	TypeTraits<std::decay_t<T>>::construct(ctx, std::forward<T>(value));
+}
+
+/**
+ * Sign the given object with the name from T.
+ *
+ * This is automatically done for when constructing/pushing object with Shared and Pointer helpers, however you need
+ * to manually add it when using inheritance.
+ */
+template <typename T>
+inline void sign(ContextPtr ctx, Index index)
+{
+	StackAssert sa(ctx, 0);
+
+	index = duk_normalize_index(ctx, index);
+
+	duk_push_string(ctx, TypeTraits<T>::name().c_str());
+	duk_push_boolean(ctx, true);
+	duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE);
+
+	/* Do for inherited classes */
+	for (const std::string &parent : TypeTraits<T>::inherits()) {
+		duk_push_string(ctx, parent.c_str());
+		duk_push_boolean(ctx, true);
+		duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE);
+	}
+}
+
+/**
+ * Check if the object at the given index is signed by T or raise TypeError if not.
+ *
+ * @param ctx the context
+ * @param index the index
+ * @see sign
+ */
+template <typename T>
+inline void checkSignature(ContextPtr ctx, Index index)
+{
+	StackAssert sa(ctx, 0);
+
+	if (!is<Object>(ctx, index) || !getProperty<bool>(ctx, index, TypeTraits<T>::name()))
+		raise(ctx, DUK_ERR_TYPE_ERROR, "invalid this binding");
+}
+
+/**
+ * Tells if the object at the specified index is of type T.
+ *
+ * @param ctx the context
+ * @param index the index
+ */
+template <typename T>
+inline bool isSigned(ContextPtr ctx, Index index)
+{
+	StackAssert sa(ctx, 0);
+
+	return is<Object>(ctx, index) && getProperty<bool>(ctx, index, TypeTraits<T>::name());
+}
+
+/* ------------------------------------------------------------------
+ * Exception handling
+ * ------------------------------------------------------------------ */
+
+/**
+ * @class Error
+ * @brief Base ECMAScript error class.
+ * @warning Override the function create for your own exceptions
+ */
+class Error {
+private:
+	int m_type{DUK_ERR_ERROR};
+	std::string m_message;
+
+protected:
+	/**
+	 * Constructor with a type of error specified, specially designed for derived errors.
+	 *
+	 * @param type of error (e.g. DUK_ERR_ERROR)
+	 * @param message the message
+	 */
+	inline Error(int type, std::string message) noexcept
+		: m_type(type)
+		, m_message(std::move(message))
+	{
+	}
+
+public:
+	/**
+	 * Constructor with a message.
+	 *
+	 * @param message the message
+	 */
+	inline Error(std::string message) noexcept
+		: m_message(std::move(message))
+	{
+	}
+
+	/**
+	 * Create the exception on the stack.
+	 *
+	 * @note the default implementation search for the global variables
+	 * @param ctx the context
+	 */
+	virtual void raise(ContextPtr ctx) const noexcept
+	{
+		duk_error(ctx, m_type, "%s", m_message.c_str());
+	}
+};
+
+/**
+ * @class EvalError
+ * @brief Error in eval() function.
+ */
+class EvalError : public Error {
+public:
+	/**
+	 * Construct an EvalError.
+	 *
+	 * @param message the message
+	 */
+	inline EvalError(std::string message) noexcept
+		: Error(DUK_ERR_EVAL_ERROR, std::move(message))
+	{
+	}
+};
+
+/**
+ * @class RangeError
+ * @brief Value is out of range.
+ */
+class RangeError : public Error {
+public:
+	/**
+	 * Construct an RangeError.
+	 *
+	 * @param message the message
+	 */
+	inline RangeError(std::string message) noexcept
+		: Error(DUK_ERR_RANGE_ERROR, std::move(message))
+	{
+	}
+};
+
+/**
+ * @class ReferenceError
+ * @brief Trying to use a variable that does not exist.
+ */
+class ReferenceError : public Error {
+public:
+	/**
+	 * Construct an ReferenceError.
+	 *
+	 * @param message the message
+	 */
+	inline ReferenceError(std::string message) noexcept
+		: Error(DUK_ERR_REFERENCE_ERROR, std::move(message))
+	{
+	}
+};
+
+/**
+ * @class SyntaxError
+ * @brief Syntax error in the script.
+ */
+class SyntaxError : public Error {
+public:
+	/**
+	 * Construct an SyntaxError.
+	 *
+	 * @param message the message
+	 */
+	inline SyntaxError(std::string message) noexcept
+		: Error(DUK_ERR_SYNTAX_ERROR, std::move(message))
+	{
+	}
+};
+
+/**
+ * @class TypeError
+ * @brief Invalid type given.
+ */
+class TypeError : public Error {
+public:
+	/**
+	 * Construct an TypeError.
+	 *
+	 * @param message the message
+	 */
+	inline TypeError(std::string message) noexcept
+		: Error(DUK_ERR_TYPE_ERROR, std::move(message))
+	{
+	}
+};
+
+/**
+ * @class URIError
+ * @brief URI manipulation failure.
+ */
+class URIError : public Error {
+public:
+	/**
+	 * Construct an URIError.
+	 *
+	 * @param message the message
+	 */
+	inline URIError(std::string message) noexcept
+		: Error(DUK_ERR_URI_ERROR, std::move(message))
+	{
+	}
+};
+
+/* ------------------------------------------------------------------
+ * Standard overloads for TypeTraits<T>
+ * ------------------------------------------------------------------ */
+
+/**
+ * @class TypeTraits<int>
+ * @brief Default implementation for int.
+ *
+ * Provides: get, is, optional, push, require.
+ */
+template <>
+class TypeTraits<int> {
+public:
+	/**
+	 * Get an integer, return 0 if not an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the integer
+	 */
+	static inline int get(ContextPtr ctx, int index)
+	{
+		return duk_get_int(ctx, index);
+	}
+
+	/**
+	 * Check if value is an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if integer
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_number(ctx, index);
+	}
+
+	/**
+	 * Get an integer, return defaultValue if the value is not an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the integer or defaultValue
+	 */
+	static inline int optional(ContextPtr ctx, int index, int defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue;
+	}
+
+	/**
+	 * Push an integer.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, int value)
+	{
+		duk_push_int(ctx, value);
+	}
+
+	/**
+	 * Require an integer, throws a JavaScript exception if not an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the integer
+	 */
+	static inline int require(ContextPtr ctx, int index)
+	{
+		return duk_require_int(ctx, index);
+	}
+};
+
+/**
+ * @class TypeTraits<bool>
+ * @brief Default implementation for bool.
+ *
+ * Provides: get, is, optional, push, require.
+ */
+template <>
+class TypeTraits<bool> {
+public:
+	/**
+	 * Get a boolean, return 0 if not a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the boolean
+	 */
+	static inline bool get(ContextPtr ctx, int index)
+	{
+		return duk_get_boolean(ctx, index);
+	}
+
+	/**
+	 * Check if value is a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if boolean
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_boolean(ctx, index);
+	}
+
+	/**
+	 * Get a bool, return defaultValue if the value is not a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the boolean or defaultValue
+	 */
+	static inline bool optional(ContextPtr ctx, int index, bool defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue;
+	}
+
+	/**
+	 * Push a boolean.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, bool value)
+	{
+		duk_push_boolean(ctx, value);
+	}
+
+	/**
+	 * Require a boolean, throws a JavaScript exception if not a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the boolean
+	 */
+	static inline bool require(ContextPtr ctx, int index)
+	{
+		return duk_require_boolean(ctx, index);
+	}
+};
+
+/**
+ * @class TypeTraits<double>
+ * @brief Default implementation for double.
+ *
+ * Provides: get, is, optional, push, require.
+ */
+template <>
+class TypeTraits<double> {
+public:
+	/**
+	 * Get a double, return 0 if not a double.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the double
+	 */
+	static inline double get(ContextPtr ctx, int index)
+	{
+		return duk_get_number(ctx, index);
+	}
+
+	/**
+	 * Check if value is a double.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if double
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_number(ctx, index);
+	}
+
+	/**
+	 * Get a double, return defaultValue if the value is not a double.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the double or defaultValue
+	 */
+	static inline double optional(ContextPtr ctx, int index, double defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue;
+	}
+
+	/**
+	 * Push a double.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, double value)
+	{
+		duk_push_number(ctx, value);
+	}
+
+	/**
+	 * Require a double, throws a JavaScript exception if not a double.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the double
+	 */
+	static inline double require(ContextPtr ctx, int index)
+	{
+		return duk_require_number(ctx, index);
+	}
+};
+
+/**
+ * @class TypeTraits<std::string>
+ * @brief Default implementation for std::string.
+ *
+ * Provides: get, is, optional, push, require.
+ *
+ * Note: the functions allows embedded '\0'.
+ */
+template <>
+class TypeTraits<std::string> {
+public:
+	/**
+	 * Get a string, return 0 if not a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the string
+	 */
+	static inline std::string get(ContextPtr ctx, int index)
+	{
+		duk_size_t size;
+		const char *text = duk_get_lstring(ctx, index, &size);
+
+		return std::string{text, size};
+	}
+
+	/**
+	 * Check if value is a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if string
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_string(ctx, index);
+	}
+
+	/**
+	 * Get a string, return defaultValue if the value is not an string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the string or defaultValue
+	 */
+	static inline std::string optional(ContextPtr ctx, int index, std::string defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue;
+	}
+
+	/**
+	 * Push a string.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, const std::string &value)
+	{
+		duk_push_lstring(ctx, value.c_str(), value.length());
+	}
+
+	/**
+	 * Require a string, throws a JavaScript exception if not a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the string
+	 */
+	static inline std::string require(ContextPtr ctx, int index)
+	{
+		duk_size_t size;
+		const char *text = duk_require_lstring(ctx, index, &size);
+
+		return std::string{text, size};
+	}
+};
+
+/**
+ * @class TypeTraits<const char *>
+ * @brief Default implementation for const char literals.
+ *
+ * Provides: get, is, optional, push, require.
+ */
+template <>
+class TypeTraits<const char *> {
+public:
+	/**
+	 * Get a string, return 0 if not a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the string
+	 */
+	static inline const char *get(ContextPtr ctx, int index)
+	{
+		return duk_get_string(ctx, index);
+	}
+
+	/**
+	 * Check if value is a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if string
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_string(ctx, index);
+	}
+
+	/**
+	 * Get an integer, return defaultValue if the value is not an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the integer or defaultValue
+	 */
+	static inline const char *optional(ContextPtr ctx, int index, const char *defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue;
+	}
+
+	/**
+	 * Push a string.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, const char *value)
+	{
+		duk_push_string(ctx, value);
+	}
+
+	/**
+	 * Require a string, throws a JavaScript exception if not a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the string
+	 */
+	static inline const char *require(ContextPtr ctx, int index)
+	{
+		return duk_require_string(ctx, index);
+	}
+};
+
+/**
+ * @brief Implementation for non-managed pointers.
+ *
+ * Provides: get, is, optional, push, require.
+ */
+template <typename T>
+class TypeTraits<RawPointer<T>> {
+public:
+	/**
+	 * Get a pointer, return nullptr if not a pointer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the pointer
+	 */
+	static inline T *get(ContextPtr ctx, int index)
+	{
+		return static_cast<T *>(duk_to_pointer(ctx, index));
+	}
+
+	/**
+	 * Check if value is a pointer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if pointer
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_pointer(ctx, index);
+	}
+
+	/**
+	 * Get a pointer, return defaultValue if the value is not a pointer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param defaultValue the defaultValue
+	 * @return the pointer or defaultValue
+	 */
+	static inline T *optional(ContextPtr ctx, int index, RawPointer<T> defaultValue)
+	{
+		return is(ctx, index) ? get(ctx, index) : defaultValue.object;
+	}
+
+	/**
+	 * Push a pointer.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static inline void push(ContextPtr ctx, const RawPointer<T> &value)
+	{
+		duk_push_pointer(ctx, value.object);
+	}
+
+	/**
+	 * Require a pointer, throws a JavaScript exception if not a pointer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return the pointer
+	 */
+	static inline T *require(ContextPtr ctx, int index)
+	{
+		return static_cast<T *>(duk_require_pointer(ctx, index));
+	}
+};
+
+/**
+ * @class TypeTraits<Function>
+ * @brief Push C++ function to the stack.
+ *
+ * Provides: push.
+ *
+ * This implementation push a Duktape/C function that is wrapped as C++ for convenience.
+ */
+template <>
+class TypeTraits<Function> {
+public:
+	/**
+	 * Check if the value at the given index is callable.
+	 *
+	 * @param ctx the context
+	 * @param index the value index
+	 * @return true if the value is callable
+	 */
+	static bool is(ContextPtr ctx, Index index)
+	{
+		return duk_is_callable(ctx, index);
+	}
+
+	/**
+	 * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the
+	 * std::function.
+	 *
+	 * @param ctx the context
+	 * @param fn the function
+	 */
+	static void push(ContextPtr ctx, Function fn)
+	{
+		duk_push_c_function(ctx, fn.function, fn.nargs);
+	}
+};
+
+/**
+ * @class TypeTraits<FunctionMap>
+ * @brief Put the functions to the object at the top of the stack.
+ *
+ * Provides: push.
+ */
+template <>
+class TypeTraits<FunctionMap> {
+public:
+	/**
+	 * Push all functions to the object at the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param map the map of function
+	 */
+	static inline void push(ContextPtr ctx, const FunctionMap &map)
+	{
+		StackAssert sa(ctx, 0);
+
+		for (const auto &entry : map) {
+			duk_push_c_function(ctx, entry.second.function, entry.second.nargs);
+			duk_put_prop_string(ctx, -2, entry.first.c_str());
+		}
+	}
+};
+
+/**
+ * @class TypeTraits<Object>
+ * @brief Push empty object to the stack.
+ *
+ * Provides: is, push.
+ */
+template <>
+class TypeTraits<Object> {
+public:
+	/**
+	 * Check if value is an object.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if object
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_object(ctx, index);
+	}
+
+	/**
+	 * Create an empty object on the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const Object &)
+	{
+		duk_push_object(ctx);
+	}
+};
+
+/**
+ * @class TypeTraits<Array>
+ * @brief Push empty array to the stack.
+ *
+ * Provides: is, push.
+ */
+template <>
+class TypeTraits<Array> {
+public:
+	/**
+	 * Check if value is a array.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if array
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_array(ctx, index);
+	}
+
+	/**
+	 * Create an empty array on the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const Array &)
+	{
+		duk_push_array(ctx);
+	}
+};
+
+/**
+ * @class TypeTraits<Undefined>
+ * @brief Push undefined value to the stack.
+ *
+ * Provides: is, push.
+ */
+template <>
+class TypeTraits<Undefined> {
+public:
+	/**
+	 * Check if value is undefined.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if undefined
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_undefined(ctx, index);
+	}
+
+	/**
+	 * Push undefined value on the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const Undefined &)
+	{
+		duk_push_undefined(ctx);
+	}
+};
+
+/**
+ * @class TypeTraits<Null>
+ * @brief Push null value to the stack.
+ *
+ * Provides: is, push.
+ */
+template <>
+class TypeTraits<Null> {
+public:
+	/**
+	 * Check if value is null.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @return true if null
+	 */
+	static inline bool is(ContextPtr ctx, int index)
+	{
+		return duk_is_null(ctx, index);
+	}
+
+	/**
+	 * Push null value on the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const Null &)
+	{
+		duk_push_null(ctx);
+	}
+};
+
+/**
+ * @brief Push this binding into the stack.
+ *
+ * Provides: push.
+ */
+template <>
+class TypeTraits<This> {
+public:
+	/**
+	 * Push this function into the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const This &)
+	{
+		duk_push_this(ctx);
+	}
+};
+
+/**
+ * @class TypeTraits<Global>
+ * @brief Push the global object to the stack.
+ *
+ * Provides: push.
+ */
+template <>
+class TypeTraits<Global> {
+public:
+	/**
+	 * Push the global object into the stack.
+	 *
+	 * @param ctx the context
+	 */
+	static inline void push(ContextPtr ctx, const Global &)
+	{
+		duk_push_global_object(ctx);
+	}
+};
+
+/**
+ * @brief Push a map of key-value pair as objects.
+ *
+ * Provides: push.
+ *
+ * This class is convenient for settings constants such as enums, string and such.
+ */
+template <typename T>
+class TypeTraits<std::unordered_map<std::string, T>> {
+public:
+	/**
+	 * Put all values from the map as properties to the object at the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param map the values
+	 * @note You need an object at the top of the stack before calling this function
+	 */
+	static void push(ContextPtr ctx, const std::unordered_map<std::string, T> &map)
+	{
+		StackAssert sa(ctx, 0);
+
+		for (const auto &pair : map) {
+			TypeTraits<T>::push(ctx, pair.second);
+			duk_put_prop_string(ctx, -2, pair.first.c_str());
+		}
+	}
+};
+
+/**
+ * @brief Push or get vectors as JavaScript arrays.
+ *
+ * Provides: get, push.
+ */
+template <typename T>
+class TypeTraits<std::vector<T>> {
+public:
+	/**
+	 * Get an array from the stack.
+	 *
+	 * @param ctx the context
+	 * @param index the array index
+	 * @return the array or empty array if the value is not an array
+	 */
+	static std::vector<T> get(ContextPtr ctx, int index)
+	{
+		StackAssert sa(ctx, 0);
+
+		std::vector<T> result;
+
+		if (!duk_is_array(ctx, -1))
+			return result;
+
+		int total = duk_get_length(ctx, index);
+
+		for (int i = 0; i < total; ++i)
+			result.push_back(getProperty<T>(ctx, index, i));
+
+		return result;
+	}
+
+	/**
+	 * Create an array with the specified values.
+	 *
+	 * @param ctx the context
+	 * @param array the values
+	 */
+	static void push(ContextPtr ctx, const std::vector<T> &array)
+	{
+		StackAssert sa(ctx, 1);
+
+		duk_push_array(ctx);
+
+		unsigned i = 0;
+		for (const auto &v : array) {
+			TypeTraits<T>::push(ctx, v);
+			duk_put_prop_index(ctx, -2, i++);
+		}
+	}
+};
+
+/**
+ * @brief Implementation of managed shared_ptr
+ * @see Shared
+ */
+template <typename T>
+class TypeTraits<Shared<T>> {
+private:
+	static void apply(ContextPtr ctx, std::shared_ptr<T> value)
+	{
+		StackAssert sa(ctx, 0);
+
+		sign<T>(ctx, -1);
+
+		duk_push_pointer(ctx, new std::shared_ptr<T>(std::move(value)));
+		duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr");
+		duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret {
+			duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr");
+			delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
+			duk_pop(ctx);
+			duk_push_null(ctx);
+			duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr");
+
+			return 0;
+		}, 1);
+		duk_set_finalizer(ctx, -2);
+	}
+
+public:
+	/**
+	 * Construct the shared_ptr as this.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static void construct(ContextPtr ctx, Shared<T> value)
+	{
+		StackAssert sa(ctx, 0);
+
+		duk_push_this(ctx);
+		apply(ctx, std::move(value.object));
+		duk_pop(ctx);
+	}
+
+	/**
+	 * Push a managed shared_ptr as object.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static void push(ContextPtr ctx, Shared<T> value)
+	{
+		StackAssert sa(ctx, 1);
+
+		duk_push_object(ctx);
+		apply(ctx, value.object);
+		TypeTraits<T>::prototype(ctx);
+		duk_set_prototype(ctx, -2);
+	}
+
+	/**
+	 * Get a managed shared_ptr from the stack.
+	 *
+	 * @param ctx the context
+	 * @param index the object index
+	 * @return the shared_ptr
+	 */
+	static std::shared_ptr<T> get(ContextPtr ctx, int index)
+	{
+		StackAssert sa(ctx, 0);
+
+		checkSignature<T>(ctx, index);
+
+		duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr");
+		std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
+		duk_pop(ctx);
+
+		return value;
+	}
+};
+
+/**
+ * @brief Implementation of managed pointers
+ * @see Pointer
+ */
+template <typename T>
+class TypeTraits<Pointer<T>> {
+private:
+	static void apply(ContextPtr ctx, T *value)
+	{
+		StackAssert sa(ctx, 0);
+
+		sign<T>(ctx, -1);
+
+		duk_push_pointer(ctx, value);
+		duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr");
+		duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret {
+			duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr");
+			delete static_cast<T *>(duk_to_pointer(ctx, -1));
+			duk_pop(ctx);
+			duk_push_null(ctx);
+			duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr");
+
+			return 0;
+		}, 1);
+		duk_set_finalizer(ctx, -2);
+	}
+
+public:
+	/**
+	 * Construct the pointer as this.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static void construct(ContextPtr ctx, Pointer<T> value)
+	{
+		StackAssert sa(ctx, 0);
+
+		duk_push_this(ctx);
+		apply(ctx, value.object);
+		duk_pop(ctx);
+	}
+
+	/**
+	 * Push a managed pointer as object.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	static void push(ContextPtr ctx, Pointer<T> value)
+	{
+		StackAssert sa(ctx, 1);
+
+		duk_push_object(ctx);
+		apply(ctx, value.object);
+		TypeTraits<T>::prototype(ctx);
+		duk_set_prototype(ctx, -2);
+	}
+
+	/**
+	 * Get a managed pointer from the stack.
+	 *
+	 * @param ctx the context
+	 * @param index the object index
+	 * @return the pointer
+	 * @warning Do not store the pointer into the C++ side, the object can be deleted at any time
+	 */
+	static T *get(ContextPtr ctx, int index)
+	{
+		StackAssert sa(ctx, 0);
+
+		checkSignature<T>(ctx, index);
+
+		duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr");
+		T *value = static_cast<T *>(duk_to_pointer(ctx, -1));
+		duk_pop(ctx);
+
+		return value;
+	}
+};
+
+} // !duk
+
+} // !irccd
+
+#endif // !JS_H
--- a/lib/irccd/js/CMakeSources.cmake	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-set(
-	JS_HEADERS
-	${CMAKE_CURRENT_LIST_DIR}/directory.h
-	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h
-	${CMAKE_CURRENT_LIST_DIR}/file.h
-	${CMAKE_CURRENT_LIST_DIR}/irccd.h
-	${CMAKE_CURRENT_LIST_DIR}/js.h
-	${CMAKE_CURRENT_LIST_DIR}/logger.h
-	${CMAKE_CURRENT_LIST_DIR}/plugin.h
-	${CMAKE_CURRENT_LIST_DIR}/server.h
-	${CMAKE_CURRENT_LIST_DIR}/system.h
-	${CMAKE_CURRENT_LIST_DIR}/timer.h
-	${CMAKE_CURRENT_LIST_DIR}/unicode.h
-	${CMAKE_CURRENT_LIST_DIR}/util.h
-)
-
-set(
-	JS_SOURCES
-	${CMAKE_CURRENT_LIST_DIR}/directory.cpp
-	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp
-	${CMAKE_CURRENT_LIST_DIR}/file.cpp
-	${CMAKE_CURRENT_LIST_DIR}/irccd.cpp
-	${CMAKE_CURRENT_LIST_DIR}/js.cpp
-	${CMAKE_CURRENT_LIST_DIR}/logger.cpp
-	${CMAKE_CURRENT_LIST_DIR}/plugin.cpp
-	${CMAKE_CURRENT_LIST_DIR}/server.cpp
-	${CMAKE_CURRENT_LIST_DIR}/system.cpp
-	${CMAKE_CURRENT_LIST_DIR}/timer.cpp
-	${CMAKE_CURRENT_LIST_DIR}/unicode.cpp
-	${CMAKE_CURRENT_LIST_DIR}/util.cpp
-)
\ No newline at end of file
--- a/lib/irccd/js/directory.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,389 +0,0 @@
-/*
- * js-directory.cpp -- Irccd.Directory API
- *
- * 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 <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <fstream>
-#include <regex>
-#include <stdexcept>
-#include <string>
-
-#include <irccd-config.h>
-
-#include <irccd/private/directory.h>
-#include <irccd/private/filesystem.h>
-#include <irccd/path.h>
-
-#include <irccd/js/js.h>
-#include <irccd/js/irccd.h>
-
-namespace irccd {
-
-class JsDirectory : public Directory {
-private:
-	std::string m_path;
-
-public:
-	inline JsDirectory(std::string path, int flags)
-		: Directory(path, flags)
-		, m_path(std::move(path))
-	{
-	}
-
-	inline const std::string &path() const noexcept
-	{
-		return m_path;
-	}
-
-	static inline const char *name() noexcept
-	{
-		return "\xff""\xff""Directory";
-	}
-};
-
-namespace {
-
-/*
- * Find an entry recursively (or not) in a directory using a predicate
- * which can be used to test for regular expression, equality.
- *
- * Do not use this function directly, use:
- *
- * - findName
- * - findRegex
- */
-template <typename Pred>
-std::string findPath(const std::string &base, bool recursive, Pred pred)
-{
-	/*
-	 * For performance reason, we first iterate over all entries that are
-	 * not directories to avoid going deeper recursively if the requested
-	 * file is in the current directory.
-	 */
-	Directory directory(base);
-
-	for (const DirectoryEntry &entry : directory)
-		if (entry.type != DirectoryEntry::Dir && pred(entry.name))
-			return base + entry.name;
-
-	if (!recursive)
-		throw std::out_of_range("entry not found");
-
-	for (const DirectoryEntry &entry : directory) {
-		if (entry.type == DirectoryEntry::Dir) {
-			std::string next = base + entry.name + fs::Separator;
-			std::string path = findPath(next, true, pred);
-
-			if (!path.empty())
-				return path;
-		}
-	}
-
-	return "";
-}
-
-/*
- * Helper for finding by equality.
- */
-std::string findName(std::string base, const std::string &pattern, bool recursive)
-{
-	return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
-		return pattern == entryname;
-	});
-}
-
-/*
- * Helper for finding by regular expression
- */
-std::string findRegex(const std::string &base, std::string pattern, bool recursive)
-{
-	std::regex regexp(pattern, std::regex::ECMAScript);
-	std::smatch smatch;
-
-	return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
-		return std::regex_match(entryname, smatch, regexp);
-	});
-}
-
-/*
- * Generic find function for:
- *
- * - Directory.find
- * - Directory.prototype.find
- *
- * The patternIndex is the argument where to test if the argument is a regex or a string.
- */
-int find(js::Context &ctx, std::string base, bool recursive, int patternIndex)
-{
-	base = path::clean(base);
-
-	try {
-		std::string path;
-
-		if (ctx.is<std::string>(patternIndex)) {
-			path = findName(base, ctx.get<std::string>(patternIndex), recursive);
-		} else {
-			/* Check if it's a valid RegExp object */
-			ctx.getGlobal<void>("RegExp");
-
-			bool isRegex = ctx.instanceof(patternIndex, -1);
-
-			ctx.pop();
-
-			if (isRegex)
-				path = findRegex(base, ctx.getProperty<std::string>(patternIndex, "source"), recursive);
-			else
-				ctx.raise(js::TypeError{"pattern must be a string or a regex expression"});
-		}
-
-		if (path.empty())
-			return 0;
-
-		ctx.push(path);
-	} catch (const std::exception &ex) {
-		ctx.raise(js::Error{ex.what()});
-	}
-
-	return 1;
-}
-
-/*
- * Generic remove function for:
- *
- * - Directory.remove
- * - Directory.prototype.remove
- */
-int remove(js::Context &ctx, const std::string &path, bool recursive)
-{
-	if (!recursive) {
-		::remove(path.c_str());
-	} else {
-		try {
-			Directory directory(path);
-
-			for (const DirectoryEntry &entry : directory) {
-				if (entry.type == DirectoryEntry::Dir) {
-					(void)remove(ctx, path + fs::Separator + entry.name, true);
-				} else {
-					std::string filename = path + fs::Separator + entry.name;
-
-					::remove(filename.c_str());
-				}
-			}
-
-			::remove(path.c_str());
-		} catch (const std::exception &) {
-			// TODO: put the error in a log.
-		}
-	}
-
-	return 0;
-}
-
-/*
- * Method: Directory.find(pattern, recursive)
- * --------------------------------------------------------
- *
- * Synonym of Directory.find(path, pattern, recursive) but the path is taken
- * from the directory object.
- *
- * Arguments:
- *   - pattern, the regular expression or file name,
- *   - recursive, set to true to search recursively (default: false).
- * Returns:
- *   The path to the file or undefined on errors or not found
- */
-int methodFind(js::Context &ctx)
-{
-	return find(ctx, ctx.self<js::Pointer<JsDirectory>>()->path(), ctx.optional<bool>(1, false), 0);
-}
-
-/*
- * Method: Directory.remove(recursive)
- * --------------------------------------------------------
- *
- * Synonym of Directory.remove(recursive) but the path is taken from the
- * directory object.
- *
- * Arguments:
- *   - recursive, recursively or not (default: false).
- * Throws:
- *   - Any exception on error.
- */
-int methodRemove(js::Context &ctx)
-{
-	return remove(ctx, ctx.self<js::Pointer<JsDirectory>>()->path(), ctx.optional<bool>(0, false));
-}
-
-const js::FunctionMap methods{
-	{ "find",		{ methodFind,		DUK_VARARGS	} },
-	{ "remove",		{ methodRemove,		1		} }
-};
-
-/* --------------------------------------------------------
- * Directory "static" functions
- * -------------------------------------------------------- */
-
-/*
- * Function: Irccd.Directory(path, flags) [constructor]
- * --------------------------------------------------------
- *
- * Opens and read the directory at the specified path.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - flags, the optional flags (default: 0).
- * Throws:
- *   - Any exception on error
- */
-int constructor(js::Context &ctx)
-{
-	if (!duk_is_constructor_call(ctx))
-		return 0;
-
-	try {
-		JsDirectory *directory = new JsDirectory(ctx.require<std::string>(0), ctx.optional<int>(1, 0));
-
-		ctx.construct(js::Pointer<JsDirectory>{directory});
-		ctx.push(js::This{});
-		ctx.push("count");
-		ctx.push(directory->count());
-		ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-		ctx.push("path");
-		ctx.push(directory->path());
-		ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-
-		/* add entries */
-		ctx.push("entries");
-		ctx.push(js::Array{});
-
-		int i = 0;
-		for (const DirectoryEntry &entry : (*directory)) {
-			ctx.push(js::Object{});
-			ctx.putProperty(-1, "name", entry.name);
-			ctx.putProperty(-1, "type", static_cast<int>(entry.type));
-			ctx.putProperty(-2, i++);
-		}
-
-		ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-	} catch (const std::exception &ex) {
-		ctx.raise(SystemError(errno, ex.what()));
-	}
-
-	return 0;
-}
-
-/*
- * Function: Irccd.Directory.find(path, pattern, recursive)
- * --------------------------------------------------------
- *
- * Find an entry by a pattern or a regular expression.
- *
- * Arguments:
- *   - path, the base path,
- *   - pattern, the regular expression or file name,
- *   - recursive, set to true to search recursively (default: false).
- * Returns:
- *   The path to the file or undefined on errors or not found.
- */
-int funcFind(js::Context &ctx)
-{
-	return find(ctx, ctx.require<std::string>(0), ctx.optional<bool>(2, false), 1);
-}
-
-/*
- * Function: Irccd.Directory.remove(path, recursive)
- * --------------------------------------------------------
- *
- * Remove the directory optionally recursively.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - recursive, recursively or not (default: false).
- * Throws:
- *   - Any exception on error.
- */
-int funcRemove(js::Context &ctx)
-{
-	return remove(ctx, ctx.require<std::string>(0), ctx.optional<bool>(1, false));
-}
-
-/*
- * Function: Irccd.Directory.mkdir(path, mode = 0700)
- * --------------------------------------------------------
- *
- * Create a directory specified by path. It will create needed subdirectories
- * just like you have invoked mkdir -p.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - mode, the mode, not available on all platforms.
- * Throws:
- *   - Any exception on error.
- */
-int funcMkdir(js::Context &ctx)
-{
-	try {
-		fs::mkdir(ctx.require<std::string>(0), ctx.optional<int>(1, 0700));
-	} catch (const std::exception &ex) {
-		ctx.raise(SystemError{errno, ex.what()});
-	}
-
-	return 0;
-}
-
-const js::FunctionMap functions{
-	{ "find",		{ funcFind,	DUK_VARARGS } },
-	{ "mkdir",		{ funcMkdir,	DUK_VARARGS } },
-	{ "remove",		{ funcRemove,	DUK_VARARGS } }
-};
-
-const js::Map<int> constants{
-	{ "Dot",		static_cast<int>(Directory::Dot)		},
-	{ "DotDot",		static_cast<int>(Directory::DotDot)		},
-	{ "TypeUnknown",	static_cast<int>(DirectoryEntry::Unknown)	},
-	{ "TypeDir",		static_cast<int>(DirectoryEntry::Dir)		},
-	{ "TypeFile",		static_cast<int>(DirectoryEntry::File)		},
-	{ "TypeLink",		static_cast<int>(DirectoryEntry::Link)		}
-};
-
-} // !namespace
-
-void loadJsDirectory(js::Context &ctx) noexcept
-{
-	ctx.getGlobal<void>("Irccd");
-
-	/* File object */
-	ctx.push(js::Function{constructor, 2});
-	ctx.push(constants);
-	ctx.push(functions);
-	ctx.putProperty(-1, "separator", std::string{fs::Separator});
-
-	/* Prototype */
-	ctx.push(js::Object{});
-	ctx.push(methods);
-	ctx.putProperty(-1, "\xff""\xff""Directory", true);
-	ctx.putProperty(-2, "prototype");
-
-	/* Put Directory */
-	ctx.putProperty(-2, "Directory");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/directory.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * js-directory.h -- Irccd.Directory API
- *
- * 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_JS_DIRECTORY_H_
-#define _IRCCD_JS_DIRECTORY_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsDirectory(js::Context &ctx) noexcept;
-
-} // !irccd
-
-#endif // !_IRCCD_JS_DIRECTORY_H_
-
--- a/lib/irccd/js/elapsed-timer.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * js-elapsed-timer.cpp -- Irccd.ElapsedTimer API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/private/elapsed-timer.h>
-
-#include "elapsed-timer.h"
-#include "js.h"
-
-namespace irccd {
-
-class JsElapsedTimer : public ElapsedTimer {
-public:
-	using ElapsedTimer::ElapsedTimer;
-
-	static inline const char *name() noexcept
-	{
-		return "\xff""\xff""ElapsedTimer";
-	}
-};
-
-namespace {
-
-/*
- * Method: ElapsedTimer.pause
- * ------------------------------------------------------------------
- *
- * Pause the timer, without resetting the current elapsed time stored.
- */
-int pause(js::Context &ctx)
-{
-	ctx.self<js::Pointer<JsElapsedTimer>>()->pause();
-
-	return 0;
-}
-
-/*
- * Method: ElapsedTimer.reset
- * ------------------------------------------------------------------
- *
- * Reset the elapsed time to 0, the status is not modified.
- */
-int reset(js::Context &ctx)
-{
-	ctx.self<js::Pointer<JsElapsedTimer>>()->reset();
-
-	return 0;
-}
-
-/*
- * Method: ElapsedTimer.restart
- * ------------------------------------------------------------------
- *
- * Restart the timer without resetting the current elapsed time.
- */
-int restart(js::Context &ctx)
-{
-	ctx.self<js::Pointer<JsElapsedTimer>>()->restart();
-
-	return 0;
-}
-
-/*
- * Method: ElapsedTimer.elapsed
- * ------------------------------------------------------------------
- *
- * Get the number of elapsed milliseconds.
- *
- * Returns:
- *   The time elapsed.
- */
-int elapsed(js::Context &ctx)
-{
-	ctx.push<int>(ctx.self<js::Pointer<JsElapsedTimer>>()->elapsed());
-
-	return 1;
-}
-
-/*
- * Function: Irccd.ElapsedTimer() [constructor]
- * ------------------------------------------------------------------
- *
- * Construct a new ElapsedTimer object.
- */
-int constructor(js::Context &ctx)
-{
-	ctx.construct(js::Pointer<JsElapsedTimer>{new JsElapsedTimer});
-
-	return 0;
-}
-
-const js::FunctionMap methods{
-	{ "elapsed",	{ elapsed,	0 } },
-	{ "pause",	{ pause,	0 } },
-	{ "reset",	{ reset,	0 } },
-	{ "restart",	{ restart,	0 } }
-};
-
-} // !namespace
-
-void loadJsElapsedTimer(js::Context &ctx) noexcept
-{
-	ctx.getGlobal<void>("Irccd");
-
-	/* Timer object */
-	ctx.push(js::Function{constructor, 3});
-
-	/* Prototype */
-	ctx.push(js::Object{});
-	ctx.push(methods);
-	ctx.putProperty(-1, "\xff""\xff""ElapsedTimer", true);
-	ctx.putProperty(-2, "prototype");
-
-	/* Put Timer */
-	ctx.putProperty(-2, "ElapsedTimer");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/elapsed-timer.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * js-elapsed-timer.h -- Irccd.ElapsedTimer API
- *
- * 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_JS_ELAPSED_TIMER_H_
-#define _IRCCD_JS_ELAPSED_TIMER_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsElapsedTimer(js::Context &ctx) noexcept;
-
-} // !irccd
-
-#endif // !_IRCCD_JS_ELAPSED_TIMER_H_
-
--- a/lib/irccd/js/file.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,628 +0,0 @@
-/*
- * js-file.h -- Irccd.File API
- *
- * 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 <cerrno>
-#include <cstring>
-#include <fstream>
-
-#include <irccd-config.h>
-
-#if defined(HAVE_STAT)
-#  include <sys/types.h>
-#  include <sys/stat.h>
-#endif
-
-#include <irccd/js/irccd.h>
-#include <irccd/js/file.h>
-
-#include <irccd/private/filesystem.h>
-
-#if defined(HAVE_STAT)
-
-namespace irccd {
-
-/*
- * js::File object for Javascript I/O
- * ------------------------------------------------------------------
- */
-
-File::File(std::string path, const std::string &mode)
-	: m_path(std::move(path))
-	, m_destructor([] (std::FILE *fp) { std::fclose(fp); })
-{
-	if ((m_stream = std::fopen(m_path.c_str(), mode.c_str())) == nullptr)
-		throw std::runtime_error(std::strerror(errno));
-}
-
-File::File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept
-	: m_stream(fp)
-	, m_destructor(std::move(destructor))
-{
-	assert(m_destructor != nullptr);
-}
-
-File::~File() noexcept
-{
-	close();
-}
-
-void File::close() noexcept
-{
-	if (m_stream) {
-		m_destructor(m_stream);
-		m_stream = nullptr;
-	}
-}
-
-bool File::isClosed() noexcept
-{
-	return m_stream == nullptr;
-}
-
-void File::seek(long amount, long dir)
-{
-	if (std::fseek(m_stream, amount, dir) != 0)
-		throw std::runtime_error(std::strerror(errno));
-}
-
-unsigned File::tell()
-{
-	long pos = std::ftell(m_stream);
-
-	if (pos == -1L)
-		throw std::runtime_error(std::strerror(errno));
-
-	return pos;
-}
-
-std::string File::readline()
-{
-	std::string result;
-	int ch;
-
-	while ((ch = std::fgetc(m_stream)) != EOF && ch != '\n')
-		result += ch;
-
-	if (ch == EOF && std::ferror(m_stream))
-		throw std::runtime_error(std::strerror(errno));
-
-	return result;
-}
-
-std::string File::read(int amount)
-{
-	assert(amount != 0);
-
-	std::string result;
-	int ch;
-
-	for (int i = 0; (ch = std::fgetc(m_stream)) != EOF; ) {
-		result += ch;
-
-		if (amount > 0 && ++i == amount)
-			break;
-	}
-
-	if (ch == EOF && std::ferror(m_stream))
-		throw std::runtime_error(std::strerror(errno));
-
-	return result;
-}
-
-void File::write(const std::string &data)
-{
-	if (std::fwrite(data.c_str(), data.length(), 1, m_stream) != 1)
-		throw std::runtime_error(std::strerror(errno));
-}
-
-bool File::eof() const noexcept
-{
-	return std::feof(m_stream);
-}
-
-/*
- * js::TypeInfo specialization for struct stat
- * ------------------------------------------------------------------
- */
-
-namespace js {
-
-template <>
-class TypeInfo<struct stat> {
-public:
-	static void push(Context &ctx, const struct stat &st)
-	{
-		ctx.push(Object{});
-
-#if defined(HAVE_STAT_ST_ATIME)
-		ctx.putProperty(-2, "atime", static_cast<int>(st.st_atime));
-#endif
-#if defined(HAVE_STAT_ST_BLKSIZE)
-		ctx.putProperty(-2, "blksize", static_cast<int>(st.st_blksize));
-#endif
-#if defined(HAVE_STAT_ST_BLOCKS)
-		ctx.putProperty(-2, "blocks", static_cast<int>(st.st_blocks));
-#endif
-#if defined(HAVE_STAT_ST_CTIME)
-		ctx.putProperty(-2, "ctime", static_cast<int>(st.st_ctime));
-#endif
-#if defined(HAVE_STAT_ST_DEV)
-		ctx.putProperty(-2, "dev", static_cast<int>(st.st_dev));
-#endif
-#if defined(HAVE_STAT_ST_GID)
-		ctx.putProperty(-2, "gid", static_cast<int>(st.st_gid));
-#endif
-#if defined(HAVE_STAT_ST_INO)
-		ctx.putProperty(-2, "ino", static_cast<int>(st.st_ino));
-#endif
-#if defined(HAVE_STAT_ST_MODE)
-		ctx.putProperty(-2, "mode", static_cast<int>(st.st_mode));
-#endif
-#if defined(HAVE_STAT_ST_MTIME)
-		ctx.putProperty(-2, "mtime", static_cast<int>(st.st_mtime));
-#endif
-#if defined(HAVE_STAT_ST_NLINK)
-		ctx.putProperty(-2, "nlink", static_cast<int>(st.st_nlink));
-#endif
-#if defined(HAVE_STAT_ST_RDEV)
-		ctx.putProperty(-2, "rdev", static_cast<int>(st.st_rdev));
-#endif
-#if defined(HAVE_STAT_ST_SIZE)
-		ctx.putProperty(-2, "size", static_cast<int>(st.st_size));
-#endif
-#if defined(HAVE_STAT_ST_UID)
-		ctx.putProperty(-2, "uid", static_cast<int>(st.st_uid));
-#endif
-	}
-};
-
-} // !js
-
-namespace {
-
-#endif // !HAVE_STAT
-
-/* --------------------------------------------------------
- * File methods
- * -------------------------------------------------------- */
-
-/*
- * Method: File.basename()
- * --------------------------------------------------------
- *
- * Synonym of `Irccd.File.basename(path)` but with the path from the file.
- *
- * Returns:
- *   The base name.
- */
-int methodBasename(js::Context &ctx)
-{
-	ctx.push(fs::baseName(ctx.self<js::Pointer<File>>()->path()));
-
-	return 1;
-}
-
-/*
- * Method: File.close()
- * --------------------------------------------------------
- *
- * Force close of the file, automatically called when object is collected.
- */
-int methodClose(js::Context &ctx)
-{
-	ctx.self<js::Pointer<File>>()->close();
-
-	return 0;
-}
-
-/*
- * Method: File.dirname()
- * --------------------------------------------------------
- *
- * Synonym of `Irccd.File.dirname(path)` but with the path from the file.
- *
- * Returns:
- *   The directory name.
- */
-int methodDirname(js::Context &ctx)
-{
-	ctx.push(fs::dirName(ctx.self<js::Pointer<File>>()->path()));
-
-	return 1;
-}
-
-/*
- * Method: File.read(amount)
- * --------------------------------------------------------
- *
- * Read the specified amount of characters or the whole file.
- *
- * Arguments:
- *   - amount, the amount of characters or -1 to read all (Optional, default: -1).
- * Returns:
- *   The string.
- * Throws:
- *   - Any exception on error.
- */
-int methodRead(js::Context &ctx)
-{
-	auto amount = ctx.optional<int>(0, -1);
-	auto self = ctx.self<js::Pointer<File>>();
-
-	if (amount == 0 || self->isClosed())
-		return 0;
-
-	try {
-		ctx.push(self->read(amount));
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 1;
-}
-
-/*
- * Method: File.readline()
- * --------------------------------------------------------
- *
- * Read the next line available.
- *
- * Returns:
- *   The next line or undefined if eof.
- * Throws:
- *   - Any exception on error.
- */
-int methodReadline(js::Context &ctx)
-{
-	try {
-		auto file = ctx.self<js::Pointer<File>>();
-
-		if (file->isClosed() || file->eof())
-			return 0;
-
-		ctx.push(file->readline());
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 1;
-}
-
-/*
- * Method: File.remove()
- * --------------------------------------------------------
- *
- * Synonym of File.remove(path) but with the path from the file.
- *
- * Throws:
- *   - Any exception on error.
- */
-int methodRemove(js::Context &ctx)
-{
-	if (::remove(ctx.self<js::Pointer<File>>()->path().c_str()) < 0)
-		ctx.raise(SystemError());
-
-	return 0;
-}
-
-/*
- * Method: File.seek(type, amount)
- * --------------------------------------------------------
- *
- * Sets the position in the file.
- *
- * Arguments:
- *   - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet),
- *   - amount, the new offset.
- * Throws:
- *   - Any exception on error.
- */
-int methodSeek(js::Context &ctx)
-{
-	auto type = ctx.require<int>(0);
-	auto amount = ctx.require<int>(1);
-	auto file = ctx.self<js::Pointer<File>>();
-
-	if (file->isClosed())
-		return 0;
-
-	try {
-		file->seek(amount, type);
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 0;
-}
-
-#if defined(HAVE_STAT)
-
-/*
- * Method: File.stat() [optional]
- * --------------------------------------------------------
- *
- * Synonym of File.stat(path) but with the path from the file.
- *
- * Returns:
- *   The stat information.
- * Throws:
- *   - Any exception on error.
- */
-int methodStat(js::Context &ctx)
-{
-	struct stat st;
-	auto file = ctx.self<js::Pointer<File>>();
-
-	if (file->isClosed())
-		return 0;
-
-	if (::stat(file->path().c_str(), &st) < 0)
-		ctx.raise(SystemError());
-
-	ctx.push(st);
-
-	return 1;
-}
-
-#endif // !HAVE_STAT
-
-/*
- * Method: File.tell()
- * --------------------------------------------------------
- *
- * Get the actual position in the file.
- *
- * Returns:
- *   The position.
- * Throws:
- *   - Any exception on error.
- */
-int methodTell(js::Context &ctx)
-{
-	auto file = ctx.self<js::Pointer<File>>();
-
-	if (file->isClosed())
-		return 0;
-
-	try {
-		ctx.push(static_cast<int>(file->tell()));
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 1;
-}
-
-/*
- * Method: File.write(data)
- * --------------------------------------------------------
- *
- * Write some characters to the file.
- *
- * Arguments:
- *   - data, the character to write.
- * Throws:
- *   - Any exception on error.
- */
-int methodWrite(js::Context &ctx)
-{
-	auto file = ctx.self<js::Pointer<File>>();
-
-	if (file->isClosed())
-		return 0;
-
-	try {
-		file->write(ctx.require<std::string>(0));
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 0;
-}
-
-const js::FunctionMap methods{
-	{ "basename",	{ methodBasename,	0	} },
-	{ "close",	{ methodClose,		0	} },
-	{ "dirname",	{ methodDirname,	0	} },
-	{ "read",	{ methodRead,		1	} },
-	{ "readline",	{ methodReadline,	0	} },
-	{ "remove",	{ methodRemove,		0	} },
-	{ "seek",	{ methodSeek,		2	} },
-#if defined(HAVE_STAT)
-	{ "stat",	{ methodStat,		0	} },
-#endif
-	{ "tell",	{ methodTell,		0	} },
-	{ "write",	{ methodWrite,		1	} },
-};
-
-/* --------------------------------------------------------
- * File "static" functions
- * -------------------------------------------------------- */
-
-/*
- * Function: Irccd.File(path, mode) [constructor]
- * --------------------------------------------------------
- *
- * Open a file specified by path with the specified mode.
- *
- * Arguments:
- *   - path, the path to the file,
- *   - mode, the mode string.
- * Throws:
- *   - Any exception on error.
- */
-int constructor(js::Context &ctx)
-{
-	if (!duk_is_constructor_call(ctx))
-		return 0;
-
-	std::string path = ctx.require<std::string>(0);
-	std::string mode = ctx.require<std::string>(1);
-
-	try {
-		ctx.construct(js::Pointer<File>{new File(path, mode)});
-	} catch (const std::exception &) {
-		ctx.raise(SystemError());
-	}
-
-	return 0;
-}
-
-/*
- * Function: Irccd.File.basename(path)
- * --------------------------------------------------------
- *
- * Return the file basename as specified in `basename(3)` C function.
- *
- * Arguments:
- *   - path, the path to the file.
- * Returns:
- *   The base name.
- */
-int functionBasename(js::Context &ctx)
-{
-	ctx.push(fs::baseName(ctx.require<std::string>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.File.dirname(path)
- * --------------------------------------------------------
- *
- * Return the file directory name as specified in `dirname(3)` C function.
- *
- * Arguments:
- *   - path, the path to the file.
- * Returns:
- *   The directory name.
- */
-int functionDirname(js::Context &ctx)
-{
-	ctx.push(fs::dirName(ctx.require<std::string>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.File.exists(path)
- * --------------------------------------------------------
- *
- * Check if the file exists.
- *
- * Arguments:
- *   - path, the path to the file.
- * Returns:
- *   True if exists.
- * Throws:
- *   - Any exception if we don't have access.
- */
-int functionExists(js::Context &ctx)
-{
-	ctx.push(fs::exists(ctx.require<std::string>(0)));
-
-	return 1;
-}
-
-/*
- * function Irccd.File.remove(path)
- * --------------------------------------------------------
- *
- * Remove the file at the specified path.
- *
- * Arguments:
- *   - path, the path to the file.
- * Throws:
- *   - Any exception on error.
- */
-int functionRemove(js::Context &ctx)
-{
-	if (::remove(ctx.require<std::string>(0).c_str()) < 0)
-		ctx.raise(SystemError());
-
-	return 0;
-}
-
-#if defined(HAVE_STAT)
-
-/*
- * function Irccd.File.stat(path) [optional]
- * --------------------------------------------------------
- *
- * Get file information at the specified path.
- *
- * Arguments:
- *   - path, the path to the file.
- * Returns:
- *   The stat information.
- * Throws:
- *   - Any exception on error.
- */
-int functionStat(js::Context &ctx)
-{
-	struct stat st;
-
-	if (::stat(ctx.require<std::string>(0).c_str(), &st) < 0)
-		ctx.raise(SystemError());
-
-	ctx.push(st);
-
-	return 1;
-}
-
-#endif // !HAVE_STAT
-
-const js::FunctionMap functions{
-	{ "basename",	{ functionBasename,	1			} },
-	{ "dirname",	{ functionDirname,	1			} },
-	{ "exists",	{ functionExists,	1			} },
-	{ "remove",	{ functionRemove,	1			} },
-#if defined(HAVE_STAT)
-	{ "stat",	{ functionStat,		1			} },
-#endif
-};
-
-const js::Map<int> constants{
-	{ "SeekCur",	static_cast<int>(std::fstream::cur)	},
-	{ "SeekEnd",	static_cast<int>(std::fstream::end)	},
-	{ "SeekSet",	static_cast<int>(std::fstream::beg)	},
-};
-
-} // !namespace
-
-void loadJsFile(js::Context &ctx)
-{
-	ctx.getGlobal<void>("Irccd");
-
-	/* File object */
-	ctx.push(js::Function{constructor, 2});
-	ctx.push(constants);
-	ctx.push(functions);
-
-	/* Prototype */
-	ctx.push(js::Object{});
-	ctx.push(methods);
-	ctx.putProperty(-1, "\xff""\xff""File", true);
-	ctx.putProperty(-2, "prototype");
-
-	/* Put File */
-	ctx.putProperty(-2, "File");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/file.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * js-file.h -- Irccd.File API
- *
- * 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_JS_FILE_H_
-#define _IRCCD_JS_FILE_H_
-
-#include <cstdio>
-
-#include "js.h"
-
-namespace irccd {
-
-/**
- * @class File
- * @brief Object for Javascript to perform I/O.
- *
- * This class can be constructed to Javascript.
- *
- * It is used in:
- *
- * - Irccd.File [constructor]
- * - Irccd.System.popen (optional)
- */
-class File {
-private:
-	File(const File &) = delete;
-	File &operator=(const File &) = delete;
-
-	File(File &&) = delete;
-	File &operator=(File &&) = delete;
-
-protected:
-	std::string m_path;
-	std::FILE *m_stream;
-	std::function<void (std::FILE *)> m_destructor;
-
-public:
-	/**
-	 * Construct a file specified by path
-	 *
-	 * @param path the path
-	 * @param mode the mode string (for std::fopen)
-	 * @throw std::runtime_error on failures
-	 */
-	File(std::string path, const std::string &mode);
-
-	/**
-	 * Construct a file from a already created FILE pointer (e.g. popen).
-	 *
-	 * The class takes ownership of fp and will close it.
-	 *
-	 * @pre destructor must not be null
-	 * @param fp the file pointer
-	 */
-	File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept;
-
-	/**
-	 * Closes the file.
-	 */
-	virtual ~File() noexcept;
-
-	/**
-	 * Get the path.
-	 *
-	 * @return the path
-	 * @warning empty when constructed from the FILE constructor
-	 */
-	inline const std::string &path() const noexcept
-	{
-		return m_path;
-	}
-
-	/**
-	 * Force close, can be safely called multiple times.
-	 */
-	void close() noexcept;
-
-	/**
-	 * Tells if the file was closed.
-	 *
-	 * @return true if closed
-	 */
-	bool isClosed() noexcept;
-
-	/**
-	 * std::fseek wrapper.
-	 *
-	 * @param offset the offset
-	 * @param origin the origin (SEEK_SET, *)
-	 * @throw std::runtime_error on failure
-	 */
-	void seek(long offset, long origin);
-
-	/**
-	 * std::ftell wrapper.
-	 *
-	 * @return the position
-	 * @throw std::runtime_error on failure
-	 */
-	unsigned tell();
-
-	/**
-	 * Read until the next line and discards the \n character.
-	 *
-	 * @return the next line or empty if EOF
-	 * @throw std::runtime_error on failure
-	 */
-	std::string readline();
-
-	/**
-	 * Read the specified amount of characters.
-	 *
-	 * If amount is less than 0, the maximum is read.
-	 *
-	 * @pre amount != 0
-	 * @param amount the number of characters to read
-	 * @return the read string
-	 * @throw std::runtime_error on failure
-	 */
-	std::string read(int amount = -1);
-
-	/**
-	 * Write the string to the file.
-	 *
-	 * @param data the data to write
-	 * @throw std::runtime_error on failure
-	 */
-	void write(const std::string &data);
-
-	/**
-	 * Check if the file reached the end.
-	 *
-	 * @return true if eof
-	 */
-	bool eof() const noexcept;
-
-	/**
-	 * Get Javascript object signature.
-	 *
-	 * @return the signature
-	 */
-	static inline const char *name() noexcept
-	{
-		return "\xff""\xff""File";
-	}
-
-	/**
-	 * Get the prototype for pushing new File objects.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void prototype(js::Context &ctx)
-	{
-		js::StackAssert sa(ctx, 1);
-
-		ctx.getGlobal<void>("Irccd");
-		ctx.getProperty<void>(-1, "File");
-		ctx.getProperty<void>(-1, "prototype");
-		ctx.remove(-2);
-		ctx.remove(-2);
-	}
-};
-
-void loadJsFile(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_FILE_H_
-
--- a/lib/irccd/js/irccd.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * js-irccd.cpp -- Irccd API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd-config.h>
-
-#include <irccd/js/irccd.h>
-
-namespace irccd {
-
-SystemError::SystemError()
-	: m_errno(errno)
-	, m_message(std::strerror(m_errno))
-{
-}
-
-SystemError::SystemError(int e, std::string message)
-	: m_errno(e)
-	, m_message(std::move(message))
-{
-}
-
-void SystemError::create(js::Context &ctx) const
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.getProperty<void>(-1, "SystemError");
-	ctx.push(m_errno);
-	ctx.push(m_message);
-	duk_new(ctx, 2);
-	ctx.remove(-2);
-}
-
-int constructor(js::Context &ctx)
-{
-	ctx.push(js::This{});
-	ctx.putProperty(-1, "errno", ctx.require<int>(0));
-	ctx.putProperty(-1, "message", ctx.require<std::string>(1));
-	ctx.putProperty(-1, "name", "SystemError");
-	ctx.pop();
-
-	return 0;
-}
-
-void loadJsIrccd(js::Context &ctx)
-{
-	/* Irccd */
-	ctx.push(js::Object{});
-
-	/* Version */
-	ctx.push(js::Object{});
-	ctx.putProperty(-1, "major", IRCCD_VERSION_MAJOR);
-	ctx.putProperty(-1, "minor", IRCCD_VERSION_MINOR);
-	ctx.putProperty(-1, "patch", IRCCD_VERSION_PATCH);
-	ctx.putProperty(-2, "version");
-
-	/* Create the SystemError that inherits from Error */
-	ctx.push(js::Function{constructor, 2});
-
-	/* Prototype */
-	ctx.getGlobal<void>("Error");
-	duk_new(ctx, 0);
-	ctx.dup(-2);
-	ctx.putProperty(-2, "constructor");
-	ctx.putProperty(-2, "prototype");
-	ctx.putProperty(-2, "SystemError");
-
-	/* Set Irccd as global */
-	ctx.putGlobal("Irccd");
-}
-
-} // !irccd
--- a/lib/irccd/js/irccd.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * js-irccd.h -- Irccd API
- *
- * 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_JS_IRCCD_H_
-#define _IRCCD_JS_IRCCD_H_
-
-#include <cerrno>
-#include <cstring>
-
-#include "js.h"
-
-namespace irccd {
-
-class SystemError {
-private:
-	int m_errno;
-	std::string m_message;
-
-public:
-	SystemError();
-
-	SystemError(int e, std::string message);
-
-	void create(js::Context &ctx) const;
-};
-
-void loadJsIrccd(js::Context &);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_IRCCD_H_
--- a/lib/irccd/js/js.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * js.cpp -- JavaScript C++14 wrapper for Duktape
- *
- * 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 "js.h"
-
-using namespace std::string_literals;
-
-namespace irccd {
-
-namespace js {
-
-ErrorInfo Context::error(int index)
-{
-	ErrorInfo error;
-
-	index = duk_normalize_index(m_handle.get(), index);
-
-	error.name = optionalProperty<std::string>(index, "name", "");
-	error.message = optionalProperty<std::string>(index, "message", "");
-	error.fileName = optionalProperty<std::string>(index, "fileName", "");
-	error.lineNumber = optionalProperty<int>(index, "lineNumber", 0);
-	error.stack = optionalProperty<std::string>(index, "stack", "");
-
-	return error;
-}
-
-void Context::pcall(unsigned nargs)
-{
-	if (duk_pcall(m_handle.get(), nargs) != 0) {
-		ErrorInfo info = error(-1);
-		duk_pop(m_handle.get());
-
-		throw info;
-	}
-}
-
-void Context::peval()
-{
-	if (duk_peval(m_handle.get()) != 0) {
-		ErrorInfo info = error(-1);
-		duk_pop(m_handle.get());
-
-		throw info;
-	}
-}
-
-void TypeInfo<Function>::push(Context &ctx, Function fn)
-{
-	/* 1. Push function wrapper */
-	duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
-		Context context(ctx);
-
-		duk_push_current_function(ctx);
-		duk_get_prop_string(ctx, -1, "\xff""\xff""js-func");
-		Function *f = static_cast<Function *>(duk_to_pointer(ctx, -1));
-		duk_pop_2(ctx);
-
-		return static_cast<duk_ret_t>(f->function(context));
-	}, fn.nargs);
-
-	/* 2. Store the moved function */
-	duk_push_pointer(ctx, new Function(std::move(fn)));
-	duk_put_prop_string(ctx, -2, "\xff""\xff""js-func");
-
-	/* 3. Store deletion flags */
-	duk_push_boolean(ctx, false);
-	duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
-
-	/* 4. Push and set a finalizer */
-	duk_push_c_function(ctx, [] (duk_context *ctx) {
-		duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-
-		if (!duk_to_boolean(ctx, -1)) {
-			duk_push_boolean(ctx, true);
-			duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-			duk_get_prop_string(ctx, 0, "\xff""\xff""js-func");
-			delete static_cast<Function *>(duk_to_pointer(ctx, -1));
-			duk_pop(ctx);
-		}
-
-		duk_pop(ctx);
-
-		return 0;
-	}, 1);
-	duk_set_finalizer(ctx, -2);
-}
-
-} // !js
-
-} // !irccd
--- a/lib/irccd/js/js.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2183 +0,0 @@
-/*
- * js.h -- JavaScript C++14 wrapper for Duktape
- *
- * 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 _JS_H_
-#define _JS_H_
-
-/**
- * @file Js.h
- * @brief Bring JavaScript using Duktape
- *
- * This file provides usual Duktape function renamed and placed into `js` namespace. It also replaces error
- * code with exceptions when possible.
- *
- * For convenience, this file also provides templated functions, overloads and much more.
- */
-
-#include <cassert>
-#include <functional>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <duktape.h>
-
-namespace irccd {
-
-/**
- * Duktape C++ namespace wrapper.
- */
-namespace js {
-
-class Context;
-
-/**
- * Typedef for readability.
- */
-using ContextPtr = duk_context *;
-
-/*
- * Basic types to manipulate with the stack
- * ------------------------------------------------------------------
- *
- * The following types can be used in some of the operations like Context::push or Context::is, they are defined
- * usually as empty classes to determine the appropriate action to execute.
- *
- * For example, `ctx.push(js::Object{})` will push an empty object into the stack.
- */
-
-/**
- * @class Object
- * @brief Empty class tag for push() function.
- */
-class Object {
-};
-
-/**
- * @class Array
- * @brief Empty class tag for push() function.
- */
-class Array {
-};
-
-/**
- * @class Global
- * @brief Empty class tag to push the global object.
- */
-class Global {
-};
-
-/**
- * @class Undefined
- * @brief Empty class tag to push undefined to the stack.
- */
-class Undefined {
-};
-
-/**
- * @class Null
- * @brief Empty class tag to push null to the stack.
- */
-class Null {
-};
-
-/**
- * @class This
- * @brief Empty class tag to push this binding to the stack.
- */
-class This {
-};
-
-/**
- * @class RawPointer
- * @brief Push a non-managed pointer to Duktape, the pointer will never be deleted.
- * @note For a managed pointer with prototype, see Pointer
- */
-template <typename T>
-class RawPointer {
-public:
-	/**
-	 * The pointer to push.
-	 */
-	T *object;
-};
-
-/*
- * Extended type manipulation
- * ------------------------------------------------------------------
- *
- * The following types are different as there are no equivalent in the native Duktape API, they are available for
- * convenience.
- */
-
-/**
- * @brief Manage shared_ptr from C++ and JavaScript
- *
- * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership
- * and deletion.
- *
- * The only requirement is to have the function `void prototype(Context &ctx)` in your class T.
- */
-template <typename T>
-class Shared {
-public:
-	/**
-	 * The shared object.
-	 */
-	std::shared_ptr<T> object;
-};
-
-/**
- * @brief Manage pointers from C++ and JavaScript
- *
- * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when
- * the JavaScript garbage collectors collect them so never store a pointer created with this.
- *
- * The only requirement is to have the function `void prototype(Context &ctx)` in your class T.
- */
-template <typename T>
-class Pointer {
-public:
-	/**
-	 * The object.
-	 */
-	T *object{nullptr};
-};
-
-/**
- * @class Function
- * @brief Duktape/C function definition.
- *
- * This class wraps the std::function as a Duktape/C function by storing a copied pointer.
- */
-class Function {
-public:
-	/**
-	 * The function pointer, must not be null.
-	 */
-	std::function<int (Context &)> function;
-
-	/**
-	 * Number of args that the function takes
-	 */
-	int nargs{0};
-};
-
-/**
- * Map of functions to set on an object.
- */
-using FunctionMap = std::unordered_map<std::string, Function>;
-
-/**
- * Map of string to type, ideal for setting constants like enums.
- */
-template <typename Type>
-using Map = std::unordered_map<std::string, Type>;
-
-/**
- * @class ErrorInfo
- * @brief Error description.
- *
- * This class fills the fields got in an Error object.
- */
-class ErrorInfo : public std::exception {
-public:
-	std::string name;		//!< name of error
-	std::string message;		//!< error message
-	std::string stack;		//!< stack if available
-	std::string fileName;		//!< filename if applicable
-	int lineNumber{0};		//!< line number if applicable
-
-	/**
-	 * Get the error message. This effectively returns message field.
-	 *
-	 * @return the message
-	 */
-	const char *what() const noexcept override
-	{
-		return message.c_str();
-	}
-};
-
-/**
- * @class TypeInfo
- * @brief Type information to implement new types in JavaScript's context.
- *
- * This class depending on your needs may have the following functions:
- *
- * - `static void construct(Context &ctx, Type value)`
- * - `static Type get(Context &ctx, int index)`
- * - `static bool is(Context &ctx, int index)`
- * - `static Type optional(Context &ctx, int index, Type defaultValue)`
- * - `static void push(Context &ctx, Type value)`
- * - `static Type require(Context &ctx, int index)`
- *
- * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors).
- *
- * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the
- * stack.
- *
- * The `is` function is used in Context::is to check if the value on the stack is of type `Type`.
- *
- * The `optional` function is used in Context::optional to get a value or a replacement if not applicable.
- *
- * The `push` function is used in Context::push to usually create a new value on the stack but some specializations
- * may not (e.g. FunctionMap).
- *
- * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if
- * not applicable.
- *
- * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`.
- *
- * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`.
- */
-template <typename Type>
-class TypeInfo {
-};
-
-/**
- * @class File
- * @brief Evaluate script from file.
- * @see Context::eval
- * @see Context::peval
- */
-class File {
-public:
-	/**
-	 * Path to the file.
-	 */
-	std::string path;
-
-	/**
-	 * Evaluate the file.
-	 *
-	 * @param ctx the context
-	 */
-	inline void eval(duk_context *ctx)
-	{
-		duk_eval_file(ctx, path.c_str());
-	}
-
-	/**
-	 * Evaluate in protected mode the file.
-	 *
-	 * @param ctx the context
-	 */
-	inline int peval(duk_context *ctx)
-	{
-		return duk_peval_file(ctx, path.c_str());
-	}
-};
-
-/**
- * @class Script
- * @brief Evaluate script from raw text.
- * @see Context::eval
- * @see Context::peval
- */
-class Script {
-public:
-	/**
-	 * The script content.
-	 */
-	std::string text;
-
-	/**
-	 * Evaluate the script.
-	 *
-	 * @param ctx the context
-	 */
-	inline void eval(duk_context *ctx)
-	{
-		duk_eval_string(ctx, text.c_str());
-	}
-
-	/**
-	 * Evaluate in protected mode the script.
-	 *
-	 * @param ctx the context
-	 */
-	inline int peval(duk_context *ctx)
-	{
-		return duk_peval_string(ctx, text.c_str());
-	}
-};
-
-/**
- * @class Context
- * @brief RAII based Duktape handler.
- *
- * This class is implicitly convertible to duk_context for convenience.
- */
-class Context {
-private:
-	using Deleter = void (*)(duk_context *);
-	using Handle = std::unique_ptr<duk_context, Deleter>;
-
-	Handle m_handle;
-
-	/* Move and copy forbidden */
-	Context(const Context &) = delete;
-	Context &operator=(const Context &) = delete;
-	Context(const Context &&) = delete;
-	Context &operator=(const Context &&) = delete;
-
-public:
-	/**
-	 * Create default context.
-	 */
-	inline Context()
-		: m_handle(duk_create_heap_default(), duk_destroy_heap)
-	{
-	}
-
-	/**
-	 * Create borrowed context that will not be deleted.
-	 *
-	 * @param ctx the pointer to duk_context
-	 */
-	inline Context(ContextPtr ctx) noexcept
-		: m_handle(ctx, [] (ContextPtr) {})
-	{
-	}
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * @return the duk_context
-	 */
-	inline operator duk_context *() noexcept
-	{
-		return m_handle.get();
-	}
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * @return the duk_context
-	 */
-	inline operator duk_context *() const noexcept
-	{
-		return m_handle.get();
-	}
-
-	/*
-	 * Basic functions
-	 * ----------------------------------------------------------
-	 *
-	 * The following functions are just standard wrappers around the native Duktape C functions, they are
-	 * defined with the same signature except that for convenience some parameters have default sane values.
-	 */
-
-	/**
-	 * Call the object at the top of the stack.
-	 *
-	 * @param ctx the context
-	 * @param nargs the number of arguments
-	 * @note Non-protected
-	 */
-	inline void call(unsigned nargs = 0)
-	{
-		duk_call(m_handle.get(), nargs);
-	}
-
-	/**
-	 * Copy a value from from to to, overwriting the previous value. If either index is invalid, throws an error.
-	 *
-	 * @param from the from index
-	 * @param to the destination
-	 */
-	inline void copy(int from, int to)
-	{
-		duk_copy(m_handle.get(), from, to);
-	}
-
-	/**
-	 * Define a property.
-	 *
-	 * @param index the object index
-	 * @param flags the flags
-	 * @note Wrapper of duk_def_prop
-	 */
-	inline void defineProperty(int index, int flags)
-	{
-		duk_def_prop(m_handle.get(), index, flags);
-	}
-
-	/**
-	 * Delete a property.
-	 *
-	 * @param index the object index
-	 * @return true if deleted
-	 * @note Wrapper of duk_del_prop
-	 */
-	inline bool deleteProperty(int index)
-	{
-		return duk_del_prop(m_handle.get(), index);
-	}
-
-	/**
-	 * Delete a property by index.
-	 *
-	 * @param index the object index
-	 * @param position the property index
-	 * @return true if deleted
-	 * @note Wrapper of duk_del_prop_index
-	 */
-	inline bool deleteProperty(int index, int position)
-	{
-		return duk_del_prop_index(m_handle.get(), index, position);
-	}
-
-	/**
-	 * Delete a property by name.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @return true if deleted
-	 * @note Wrapper of duk_del_prop_string
-	 */
-	inline bool deleteProperty(int index, const std::string &name)
-	{
-		return duk_del_prop_string(m_handle.get(), index, name.c_str());
-	}
-
-	/**
-	 * Push a duplicate of value at from_index to the stack. If from_index is invalid, throws an error.
-	 *
-	 * @param index the value to copy
-	 * @note Wrapper of duk_dup
-	 */
-	inline void dup(int index = -1)
-	{
-		duk_dup(m_handle.get(), index);
-	}
-
-	/**
-	 * Evaluate a non-protected chunk that is at the top of the stack.
-	 */
-	inline void eval()
-	{
-		duk_eval(m_handle.get());
-	}
-
-	/**
-	 * Check if the object as a property.
-	 *
-	 * @param index the object index
-	 * @return true if has
-	 * @note Wrapper of duk_has_prop
-	 */
-	inline bool hasProperty(int index)
-	{
-		return duk_has_prop(m_handle.get(), index);
-	}
-
-	/**
-	 * Check if the object as a property by index.
-	 *
-	 * @param index the object index
-	 * @param position the property index
-	 * @return true if has
-	 * @note Wrapper of duk_has_prop_index
-	 */
-	inline bool hasProperty(int index, int position)
-	{
-		return duk_has_prop_index(m_handle.get(), index, position);
-	}
-
-	/**
-	 * Check if the object as a property by string
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @return true if has
-	 * @note Wrapper of duk_has_prop_string
-	 */
-	inline bool hasProperty(int index, const std::string &name)
-	{
-		return duk_has_prop_string(m_handle.get(), index, name.c_str());
-	}
-
-	/**
-	 * Check if idx1 is an instance of idx2.
-	 *
-	 * @param ctx the context
-	 * @param idx1 the value to test
-	 * @param idx2 the instance requested
-	 * @return true if idx1 is instance of idx2
-	 * @note Wrapper of duk_instanceof
-	 */
-	inline bool instanceof(int idx1, int idx2)
-	{
-		return duk_instanceof(m_handle.get(), idx1, idx2);
-	}
-
-	/**
-	 * Insert a value at to with a value popped from the stack top. The previous value at to and any values above
-	 * it are moved up the stack by a step. If to is an invalid index, throws an error.
-	 *
-	 * @note Negative indices are evaluated prior to popping the value at the stack top
-	 * @param to the destination
-	 * @note Wrapper of duk_insert
-	 */
-	inline void insert(int to)
-	{
-		duk_insert(m_handle.get(), to);
-	}
-
-	/**
-	 * Pop a certain number of values from the top of the stack.
-	 *
-	 * @param ctx the context
-	 * @param count the number of values to pop
-	 * @note Wrapper of duk_pop_n
-	 */
-	inline void pop(unsigned count = 1)
-	{
-		duk_pop_n(m_handle.get(), count);
-	}
-
-	/**
-	 * Remove value at index. Elements above index are shifted down the stack by a step. If to is an invalid index,
-	 * throws an error.
-	 *
-	 * @param index the value to remove
-	 * @note Wrapper of duk_remove
-	 */
-	inline void remove(int index)
-	{
-		duk_remove(m_handle.get(), index);
-	}
-
-	/**
-	 * Replace value at to_index with a value popped from the stack top. If to_index is an invalid index,
-	 * throws an error.
-	 *
-	 * @param index the value to replace by the value at the top of the stack
-	 * @note Negative indices are evaluated prior to popping the value at the stack top.
-	 * @note Wrapper of duk_replace
-	 */
-	inline void replace(int index)
-	{
-		duk_replace(m_handle.get(), index);
-	}
-
-	/**
-	 * Swap values at indices index1 and index2. If the indices are the same, the call is a no-op. If either index
-	 * is invalid, throws an error.
-	 *
-	 * @param index1 the first index
-	 * @param index2 the second index
-	 * @note Wrapper of duk_swap
-	 */
-	inline void swap(int index1, int index2)
-	{
-		duk_swap(m_handle.get(), index1, index2);
-	}
-
-	/**
-	 * Get the current stack size.
-	 *
-	 * @param ctx the context
-	 * @return the stack size
-	 * @note Wrapper of duk_get_top
-	 */
-	inline int top() noexcept
-	{
-		return duk_get_top(m_handle.get());
-	}
-
-	/**
-	 * Get the type of the value at the specified index.
-	 *
-	 * @param ctx the context
-	 * @param index the idnex
-	 * @return the type
-	 * @note Wrapper of duk_get_type
-	 */
-	inline int type(int index) noexcept
-	{
-		return duk_get_type(m_handle.get(), index);
-	}
-
-	/*
-	 * Extended native functions
-	 * ----------------------------------------------------------
-	 *
-	 * The following functions have different behaviour than the original native Duktape C functions, see their
-	 * descriptions for more information
-	 */
-
-	/**
-	 * Call in protected mode the object at the top of the stack.
-	 *
-	 * @param nargs the number of arguments
-	 * @throw ErrorInfo on errors
-	 * @note Wrapper of duk_pcall
-	 */
-	void pcall(unsigned nargs = 0);
-
-	/**
-	 * Evaluate a non-protected source.
-	 *
-	 * @param source the source
-	 * @see File
-	 * @see Script
-	 * @note Wrapper of duk_eval
-	 */
-	template <typename Source>
-	inline void eval(Source &&source)
-	{
-		source.eval(m_handle.get());
-	}
-
-	/**
-	 * Evaluate a protected chunk that is at the top of the stack.
-	 *
-	 * @throw ErrorInfo the error
-	 * @note Wrapper of duk_peval
-	 */
-	void peval();
-
-	/**
-	 * Evaluate a protected source.
-	 *
-	 * @param source the source
-	 * @see File
-	 * @see Script
-	 * @throw ErrorInfo on failure
-	 * @note Wrapper of duk_peval
-	 */
-	template <typename Source>
-	inline void peval(Source &&source)
-	{
-		if (source.peval(m_handle.get()) != 0) {
-			ErrorInfo info = error(-1);
-			duk_pop(m_handle.get());
-
-			throw info;
-		}
-	}
-
-	/*
-	 * Push / Get / Require / Is / Optional
-	 * ----------------------------------------------------------
-	 *
-	 * The following functions are used to push, get or check values from the stack. They use specialization
-	 * of TypeInfo class.
-	 */
-
-	/**
-	 * Push a value into the stack. Calls TypeInfo<T>::push(*this, value);
-	 *
-	 * @param value the value to forward
-	 */
-	template <typename Type>
-	inline void push(Type &&value)
-	{
-		TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value));
-	}
-
-	/**
-	 * Generic template function to get a value from the stack.
-	 *
-	 * @param index the index
-	 * @return the value
-	 */
-	template <typename Type>
-	inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0))
-	{
-		return TypeInfo<Type>::get(*this, index);
-	}
-
-	/**
-	 * Require a type at the specified index.
-	 *
-	 * @param index the index
-	 * @return the value
-	 */
-	template <typename Type>
-	inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0))
-	{
-		return TypeInfo<Type>::require(*this, index);
-	}
-
-	/**
-	 * Check if a value is a type of T.
-	 *
-	 * The TypeInfo<T> must have `static bool is(ContextPtr ptr, int index)`.
-	 *
-	 * @param index the value index
-	 * @return true if is the type
-	 */
-	template <typename T>
-	inline bool is(int index)
-	{
-		return TypeInfo<T>::is(*this, index);
-	}
-
-	/**
-	 * Get an optional value from the stack, if the value is not available of not the correct type,
-	 * return defaultValue instead.
-	 *
-	 * The TypeInfo<T> must have `static T optional(Context &, int index, T &&defaultValue)`.
-	 *
-	 * @param index the value index
-	 * @param defaultValue the value replacement
-	 * @return the value or defaultValue
-	 */
-	template <typename Type, typename DefaultValue>
-	inline auto optional(int index, DefaultValue &&defaultValue)
-	{
-		return TypeInfo<std::decay_t<Type>>::optional(*this, index, std::forward<DefaultValue>(defaultValue));
-	}
-
-	/*
-	 * Properties management
-	 * ----------------------------------------------------------
-	 *
-	 * The following functions are used to read or set properties on objects or globals also using TypeInfo.
-	 */
-
-	/**
-	 * Get the property `name' as value from the object at the specified index.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @return the value
-	 * @note The stack is unchanged
-	 */
-	template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
-	inline auto getProperty(int index, const std::string &name) -> decltype(get<Type>(0))
-	{
-		duk_get_prop_string(m_handle.get(), index, name.c_str());
-		decltype(get<Type>(0)) value = get<Type>(-1);
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	/**
-	 * Get an optional property `name` from the object at the specified index.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @param def the default value
-	 * @return the value or def
-	 * @note The stack is unchanged
-	 */
-	template <typename Type, typename DefaultValue>
-	inline auto optionalProperty(int index, const std::string &name, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def)))
-	{
-		duk_get_prop_string(m_handle.get(), index, name.c_str());
-		decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def));
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	/**
-	 * Get a property by index, for arrays.
-	 *
-	 * @param index the object index
-	 * @param position the position int the object
-	 * @return the value
-	 * @note The stack is unchanged
-	 */
-	template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
-	inline auto getProperty(int index, int position) -> decltype(get<Type>(0))
-	{
-		duk_get_prop_index(m_handle.get(), index, position);
-		decltype(get<Type>(0)) value = get<Type>(-1);
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	/**
-	 * Get an optional property by index, for arrays
-	 *
-	 * @param index the object index
-	 * @param position the position int the object
-	 * @param def the default value
-	 * @return the value or def
-	 * @note The stack is unchanged
-	 */
-	template <typename Type, typename DefaultValue>
-	inline auto optionalProperty(int index, int position, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def)))
-	{
-		duk_get_prop_index(m_handle.get(), index, position);
-		decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def));
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	/**
-	 * Get the property `name' and push it to the stack from the object at the specified index.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @note The stack contains the property value
-	 */
-	template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
-	inline void getProperty(int index, const std::string &name)
-	{
-		duk_get_prop_string(m_handle.get(), index, name.c_str());
-	}
-
-	/**
-	 * Get the property by index and push it to the stack from the object at the specified index.
-	 *
-	 * @param index the object index
-	 * @param position the position in the object
-	 * @note The stack contains the property value
-	 */
-	template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
-	inline void getProperty(int index, int position)
-	{
-		duk_get_prop_index(m_handle.get(), index, position);
-	}
-
-	/**
-	 * Set a property to the object at the specified index.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 * @param value the value to forward
-	 * @note The stack is unchanged
-	 */
-	template <typename Type>
-	void putProperty(int index, const std::string &name, Type &&value)
-	{
-		index = duk_normalize_index(m_handle.get(), index);
-
-		push(std::forward<Type>(value));
-		duk_put_prop_string(m_handle.get(), index, name.c_str());
-	}
-
-	/**
-	 * Set a property by index, for arrays.
-	 *
-	 * @param index the object index
-	 * @param position the position in the object
-	 * @param value the value to forward
-	 * @note The stack is unchanged
-	 */
-	template <typename Type>
-	void putProperty(int index, int position, Type &&value)
-	{
-		index = duk_normalize_index(m_handle.get(), index);
-
-		push(std::forward<Type>(value));
-		duk_put_prop_index(m_handle.get(), index, position);
-	}
-
-	/**
-	 * Put the value that is at the top of the stack as property to the object.
-	 *
-	 * @param index the object index
-	 * @param name the property name
-	 */
-	inline void putProperty(int index, const std::string &name)
-	{
-		duk_put_prop_string(m_handle.get(), index, name.c_str());
-	}
-
-	/**
-	 * Put the value that is at the top of the stack to the object as index.
-	 *
-	 * @param index the object index
-	 * @param position the position in the object
-	 */
-	inline void putProperty(int index, int position)
-	{
-		duk_put_prop_index(m_handle.get(), index, position);
-	}
-
-	/**
-	 * Get a global value.
-	 *
-	 * @param name the name of the global variable
-	 * @return the value
-	 */
-	template <typename Type>
-	inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0))
-	{
-		duk_get_global_string(m_handle.get(), name.c_str());
-		decltype(get<Type>(0)) value = get<Type>(-1);
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	/**
-	 * Overload that push the value at the top of the stack instead of returning it.
-	 */
-	template <typename Type>
-	inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept
-	{
-		duk_get_global_string(m_handle.get(), name.c_str());
-	}
-
-	/**
-	 * Set a global variable.
-	 *
-	 * @param name the name of the global variable
-	 * @param type the value to set
-	 */
-	template <typename Type>
-	inline void putGlobal(const std::string &name, Type&& type)
-	{
-		push(std::forward<Type>(type));
-		duk_put_global_string(m_handle.get(), name.c_str());
-	}
-
-	/**
-	 * Put the value at the top of the stack as global property.
-	 *
-	 * @param name the property name
-	 */
-	inline void putGlobal(const std::string &name)
-	{
-		duk_put_global_string(m_handle.get(), name.c_str());
-	}
-
-	/*
-	 * Extra functions
-	 * ----------------------------------------------------------
-	 *
-	 * The following functions are implemented for convenience and do not exists in the native Duktape API.
-	 */
-
-	/**
-	 * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
-	 *
-	 * @param index the index
-	 * @return the information
-	 */
-	ErrorInfo error(int index);
-
-	/**
-	 * Enumerate an object or an array at the specified index.
-	 *
-	 * @param index the object or array index
-	 * @param flags the optional flags to pass to duk_enum
-	 * @param getvalue set to true if you want to extract the value
-	 * @param func the function to call for each properties
-	 */
-	template <typename Func>
-	void enumerate(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func)
-	{
-		duk_enum(m_handle.get(), index, flags);
-
-		while (duk_next(m_handle.get(), -1, getvalue)) {
-			func(*this);
-			duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0));
-		}
-
-		duk_pop(m_handle.get());
-	}
-
-	/**
-	 * Return the this binding of the current function.
-	 *
-	 * @return the this binding as the template given
-	 */
-	template <typename T>
-	inline auto self() -> decltype(TypeInfo<T>::get(*this, 0))
-	{
-		duk_push_this(m_handle.get());
-		decltype(TypeInfo<T>::get(*this, 0)) value = TypeInfo<T>::get(*this, -1);
-		duk_pop(m_handle.get());
-
-		return value;
-	}
-
-	inline void raise()
-	{
-		duk_throw(m_handle.get());
-	}
-
-	/**
-	 * Throw an ECMAScript exception.
-	 *
-	 * @param ex the exception
-	 */
-	template <typename Exception>
-	void raise(const Exception &ex)
-	{
-		ex.create(*this);
-		raise();
-	}
-
-	/**
-	 * Construct the object in place, setting value as this binding.
-	 *
-	 * The TypeInfo<T> must have the following requirements:
-	 *
-	 * - static void construct(Context &, T): must update this with the value and keep the stack unchanged
-	 *
-	 * @param value the value to forward
-	 * @see self
-	 */
-	template <typename T>
-	inline void construct(T &&value)
-	{
-		TypeInfo<std::decay_t<T>>::construct(*this, std::forward<T>(value));
-	}
-};
-
-/**
- * @class StackAssert
- * @brief Stack sanity checker.
- *
- * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor
- * will examinate if the stack size matches the user expected size.
- *
- * When compiled with NDEBUG, this class does nothing.
- *
- * To use it, just declare an lvalue at the beginning of your function.
- */
-class StackAssert {
-#if !defined(NDEBUG)
-private:
-	Context &m_context;
-	unsigned m_expected;
-	unsigned m_begin;
-#endif
-
-public:
-	/**
-	 * Create the stack checker.
-	 *
-	 * No-op if NDEBUG is set.
-	 *
-	 * @param ctx the context
-	 * @param expected the size expected relative to the already existing values
-	 */
-	inline StackAssert(Context &ctx, unsigned expected = 0) noexcept
-#if !defined(NDEBUG)
-		: m_context(ctx)
-		, m_expected(expected)
-		, m_begin(static_cast<unsigned>(m_context.top()))
-#endif
-	{
-#if defined(NDEBUG)
-		(void)ctx;
-		(void)expected;
-#endif
-	}
-
-	/**
-	 * Verify the expected size.
-	 *
-	 * No-op if NDEBUG is set.
-	 */
-	inline ~StackAssert() noexcept
-	{
-#if !defined(NDEBUG)
-		assert(m_context.top() - m_begin == m_expected);
-#endif
-	}
-};
-
-/* ------------------------------------------------------------------
- * Exception handling
- * ------------------------------------------------------------------ */
-
-/**
- * @class Error
- * @brief Base ECMAScript error class.
- * @warning Override the function create for your own exceptions
- */
-class Error {
-protected:
-	std::string m_name;	//!< Name of exception (e.g RangeError)
-	std::string m_message;	//!< The message
-
-	/**
-	 * Constructor with a type of error specified, specially designed for derived errors.
-	 *
-	 * @param name the error name (e.g RangeError)
-	 * @param message the message
-	 */
-	inline Error(std::string name, std::string message) noexcept
-		: m_name(std::move(name))
-		, m_message(std::move(message))
-	{
-	}
-
-public:
-	/**
-	 * Constructor with a message.
-	 *
-	 * @param message the message
-	 */
-	inline Error(std::string message) noexcept
-		: m_name("Error")
-		, m_message(std::move(message))
-	{
-	}
-
-	/**
-	 * Get the error type (e.g RangeError).
-	 *
-	 * @return the name
-	 */
-	inline const std::string &name() const noexcept
-	{
-		return m_name;
-	}
-
-	/**
-	 * Create the exception on the stack.
-	 *
-	 * @note the default implementation search for the global variables
-	 * @param ctx the context
-	 */
-	virtual void create(Context &ctx) const noexcept
-	{
-		duk_get_global_string(ctx, m_name.c_str());
-		duk_push_string(ctx, m_message.c_str());
-		duk_new(ctx, 1);
-		duk_push_string(ctx, m_name.c_str());
-		duk_put_prop_string(ctx, -2, "name");
-	}
-};
-
-/**
- * @class EvalError
- * @brief Error in eval() function.
- */
-class EvalError : public Error {
-public:
-	/**
-	 * Construct an EvalError.
-	 *
-	 * @param message the message
-	 */
-	inline EvalError(std::string message) noexcept
-		: Error("EvalError", std::move(message))
-	{
-	}
-};
-
-/**
- * @class RangeError
- * @brief Value is out of range.
- */
-class RangeError : public Error {
-public:
-	/**
-	 * Construct an RangeError.
-	 *
-	 * @param message the message
-	 */
-	inline RangeError(std::string message) noexcept
-		: Error("RangeError", std::move(message))
-	{
-	}
-};
-
-/**
- * @class ReferenceError
- * @brief Trying to use a variable that does not exist.
- */
-class ReferenceError : public Error {
-public:
-	/**
-	 * Construct an ReferenceError.
-	 *
-	 * @param message the message
-	 */
-	inline ReferenceError(std::string message) noexcept
-		: Error("ReferenceError", std::move(message))
-	{
-	}
-};
-
-/**
- * @class SyntaxError
- * @brief Syntax error in the script.
- */
-class SyntaxError : public Error {
-public:
-	/**
-	 * Construct an SyntaxError.
-	 *
-	 * @param message the message
-	 */
-	inline SyntaxError(std::string message) noexcept
-		: Error("SyntaxError", std::move(message))
-	{
-	}
-};
-
-/**
- * @class TypeError
- * @brief Invalid type given.
- */
-class TypeError : public Error {
-public:
-	/**
-	 * Construct an TypeError.
-	 *
-	 * @param message the message
-	 */
-	inline TypeError(std::string message) noexcept
-		: Error("TypeError", std::move(message))
-	{
-	}
-};
-
-/**
- * @class URIError
- * @brief URI manipulation failure.
- */
-class URIError : public Error {
-public:
-	/**
-	 * Construct an URIError.
-	 *
-	 * @param message the message
-	 */
-	inline URIError(std::string message) noexcept
-		: Error("URIError", std::move(message))
-	{
-	}
-};
-
-/* ------------------------------------------------------------------
- * Standard overloads for TypeInfo<T>
- * ------------------------------------------------------------------ */
-
-/**
- * @class TypeInfo<int>
- * @brief Default implementation for int.
- *
- * Provides: get, is, optional, push, require.
- */
-template <>
-class TypeInfo<int> {
-public:
-	/**
-	 * Get an integer, return 0 if not an integer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the integer
-	 */
-	static inline int get(Context &ctx, int index)
-	{
-		return duk_get_int(ctx, index);
-	}
-
-	/**
-	 * Check if value is an integer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if integer
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_number(ctx, index);
-	}
-
-	/**
-	 * Get an integer, return defaultValue if the value is not an integer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the integer or defaultValue
-	 */
-	static inline int optional(Context &ctx, int index, int defaultValue)
-	{
-		if (!duk_is_number(ctx, index))
-			return defaultValue;
-
-		return duk_get_int(ctx, index);
-	}
-
-	/**
-	 * Push an integer.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, int value)
-	{
-		duk_push_int(ctx, value);
-	}
-
-	/**
-	 * Require an integer, throws a JavaScript exception if not an integer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the integer
-	 */
-	static inline int require(Context &ctx, int index)
-	{
-		return duk_require_int(ctx, index);
-	}
-};
-
-/**
- * @class TypeInfo<bool>
- * @brief Default implementation for bool.
- *
- * Provides: get, is, optional, push, require.
- */
-template <>
-class TypeInfo<bool> {
-public:
-	/**
-	 * Get a boolean, return 0 if not a boolean.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the boolean
-	 */
-	static inline bool get(Context &ctx, int index)
-	{
-		return duk_get_boolean(ctx, index);
-	}
-
-	/**
-	 * Check if value is a boolean.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if boolean
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_boolean(ctx, index);
-	}
-
-	/**
-	 * Get a bool, return defaultValue if the value is not a boolean.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the boolean or defaultValue
-	 */
-	static inline bool optional(Context &ctx, int index, bool defaultValue)
-	{
-		if (!duk_is_boolean(ctx, index))
-			return defaultValue;
-
-		return duk_get_boolean(ctx, index);
-	}
-
-	/**
-	 * Push a boolean.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, bool value)
-	{
-		duk_push_boolean(ctx, value);
-	}
-
-	/**
-	 * Require a boolean, throws a JavaScript exception if not a boolean.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the boolean
-	 */
-	static inline bool require(Context &ctx, int index)
-	{
-		return duk_require_boolean(ctx, index);
-	}
-};
-
-/**
- * @class TypeInfo<double>
- * @brief Default implementation for double.
- *
- * Provides: get, is, optional, push, require.
- */
-template <>
-class TypeInfo<double> {
-public:
-	/**
-	 * Get a double, return 0 if not a double.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the double
-	 */
-	static inline double get(Context &ctx, int index)
-	{
-		return duk_get_number(ctx, index);
-	}
-
-	/**
-	 * Check if value is a double.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if double
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_number(ctx, index);
-	}
-
-	/**
-	 * Get a double, return defaultValue if the value is not a double.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the double or defaultValue
-	 */
-	static inline double optional(Context &ctx, int index, double defaultValue)
-	{
-		if (!duk_is_number(ctx, index))
-			return defaultValue;
-
-		return duk_get_number(ctx, index);
-	}
-
-	/**
-	 * Push a double.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, double value)
-	{
-		duk_push_number(ctx, value);
-	}
-
-	/**
-	 * Require a double, throws a JavaScript exception if not a double.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the double
-	 */
-	static inline double require(Context &ctx, int index)
-	{
-		return duk_require_number(ctx, index);
-	}
-};
-
-/**
- * @class TypeInfo<std::string>
- * @brief Default implementation for std::string.
- *
- * Provides: get, is, optional, push, require.
- *
- * Note: the functions allows embedded '\0'.
- */
-template <>
-class TypeInfo<std::string> {
-public:
-	/**
-	 * Get a string, return 0 if not a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the string
-	 */
-	static inline std::string get(Context &ctx, int index)
-	{
-		duk_size_t size;
-		const char *text = duk_get_lstring(ctx, index, &size);
-
-		return std::string{text, size};
-	}
-
-	/**
-	 * Check if value is a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if string
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_string(ctx, index);
-	}
-
-	/**
-	 * Get a string, return defaultValue if the value is not an string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the string or defaultValue
-	 */
-	static inline std::string optional(Context &ctx, int index, std::string defaultValue)
-	{
-		if (!duk_is_string(ctx, index))
-			return defaultValue;
-
-		return get(ctx, index);
-	}
-
-	/**
-	 * Push a string.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, const std::string &value)
-	{
-		duk_push_lstring(ctx, value.c_str(), value.length());
-	}
-
-	/**
-	 * Require a string, throws a JavaScript exception if not a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the string
-	 */
-	static inline std::string require(Context &ctx, int index)
-	{
-		duk_size_t size;
-		const char *text = duk_require_lstring(ctx, index, &size);
-
-		return std::string{text, size};
-	}
-};
-
-/**
- * @class TypeInfo<const char *>
- * @brief Default implementation for const char literals.
- *
- * Provides: get, is, optional, push, require.
- */
-template <>
-class TypeInfo<const char *> {
-public:
-	/**
-	 * Get a string, return 0 if not a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the string
-	 */
-	static inline const char *get(Context &ctx, int index)
-	{
-		return duk_get_string(ctx, index);
-	}
-
-	/**
-	 * Check if value is a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if string
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_string(ctx, index);
-	}
-
-	/**
-	 * Get an integer, return defaultValue if the value is not an integer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the integer or defaultValue
-	 */
-	static inline const char *optional(Context &ctx, int index, const char *defaultValue)
-	{
-		if (!duk_is_string(ctx, index))
-			return defaultValue;
-
-		return duk_get_string(ctx, index);
-	}
-
-	/**
-	 * Push a string.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, const char *value)
-	{
-		duk_push_string(ctx, value);
-	}
-
-	/**
-	 * Require a string, throws a JavaScript exception if not a string.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the string
-	 */
-	static inline const char *require(Context &ctx, int index)
-	{
-		return duk_require_string(ctx, index);
-	}
-};
-
-/**
- * @brief Implementation for non-managed pointers.
- *
- * Provides: get, is, optional, push, require.
- */
-template <typename T>
-class TypeInfo<RawPointer<T>> {
-public:
-	/**
-	 * Get a pointer, return nullptr if not a pointer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the pointer
-	 */
-	static inline T *get(Context &ctx, int index)
-	{
-		return static_cast<T *>(duk_to_pointer(ctx, index));
-	}
-
-	/**
-	 * Check if value is a pointer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if pointer
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_pointer(ctx, index);
-	}
-
-	/**
-	 * Get a pointer, return defaultValue if the value is not a pointer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @param defaultValue the defaultValue
-	 * @return the pointer or defaultValue
-	 */
-	static inline T *optional(Context &ctx, int index, RawPointer<T> defaultValue)
-	{
-		if (!duk_is_pointer(ctx, index))
-			return defaultValue.object;
-
-		return static_cast<T *>(duk_to_pointer(ctx, index));
-	}
-
-	/**
-	 * Push a pointer.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static inline void push(Context &ctx, const RawPointer<T> &value)
-	{
-		duk_push_pointer(ctx, value.object);
-	}
-
-	/**
-	 * Require a pointer, throws a JavaScript exception if not a pointer.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return the pointer
-	 */
-	static inline T *require(Context &ctx, int index)
-	{
-		return static_cast<T *>(duk_require_pointer(ctx, index));
-	}
-};
-
-/**
- * @class TypeInfo<Function>
- * @brief Push C++ function to the stack.
- *
- * Provides: push.
- *
- * This implementation push a Duktape/C function that is wrapped as C++ for convenience.
- */
-template <>
-class TypeInfo<Function> {
-public:
-	/**
-	 * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the
-	 * std::function.
-	 *
-	 * @param ctx the context
-	 * @param fn the function
-	 */
-	static void push(Context &ctx, Function fn);
-};
-
-/**
- * @class TypeInfo<FunctionMap>
- * @brief Put the functions to the object at the top of the stack.
- *
- * Provides: push.
- */
-template <>
-class TypeInfo<FunctionMap> {
-public:
-	/**
-	 * Push a map of function to the object at the top of the stack.
-	 *
-	 * @param ctx the context
-	 * @param map the map of function
-	 */
-	static inline void push(Context &ctx, const FunctionMap &map)
-	{
-		for (const auto &entry : map)
-			ctx.putProperty(-1, entry.first, entry.second);
-	}
-};
-
-/**
- * @class TypeInfo<Object>
- * @brief Push empty object to the stack.
- *
- * Provides: is, push.
- */
-template <>
-class TypeInfo<Object> {
-public:
-	/**
-	 * Check if value is an object.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if object
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_object(ctx, index);
-	}
-
-	/**
-	 * Create an empty object on the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const Object &)
-	{
-		duk_push_object(ctx);
-	}
-};
-
-/**
- * @class TypeInfo<Array>
- * @brief Push empty array to the stack.
- *
- * Provides: is, push.
- */
-template <>
-class TypeInfo<Array> {
-public:
-	/**
-	 * Check if value is a array.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if array
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_array(ctx, index);
-	}
-
-	/**
-	 * Create an empty array on the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const Array &)
-	{
-		duk_push_array(ctx);
-	}
-};
-
-/**
- * @class TypeInfo<Undefined>
- * @brief Push undefined value to the stack.
- *
- * Provides: is, push.
- */
-template <>
-class TypeInfo<Undefined> {
-public:
-	/**
-	 * Check if value is undefined.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if undefined
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_undefined(ctx, index);
-	}
-
-	/**
-	 * Push undefined value on the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const Undefined &)
-	{
-		duk_push_undefined(ctx);
-	}
-};
-
-/**
- * @class TypeInfo<Null>
- * @brief Push null value to the stack.
- *
- * Provides: is, push.
- */
-template <>
-class TypeInfo<Null> {
-public:
-	/**
-	 * Check if value is null.
-	 *
-	 * @param ctx the context
-	 * @param index the index
-	 * @return true if null
-	 */
-	static inline bool is(Context &ctx, int index)
-	{
-		return duk_is_null(ctx, index);
-	}
-
-	/**
-	 * Push null value on the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const Null &)
-	{
-		duk_push_null(ctx);
-	}
-};
-
-/**
- * @brief Push this binding into the stack.
- *
- * Provides: push.
- */
-template <>
-class TypeInfo<This> {
-public:
-	/**
-	 * Push this function into the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const This &)
-	{
-		duk_push_this(ctx);
-	}
-};
-
-/**
- * @class TypeInfo<Global>
- * @brief Push the global object to the stack.
- *
- * Provides: push.
- */
-template <>
-class TypeInfo<Global> {
-public:
-	/**
-	 * Push the global object into the stack.
-	 *
-	 * @param ctx the context
-	 */
-	static inline void push(Context &ctx, const Global &)
-	{
-		duk_push_global_object(ctx);
-	}
-};
-
-/**
- * @brief Push a map of key-value pair as objects.
- *
- * Provides: push.
- *
- * This class is convenient for settings constants such as enums, string and such.
- */
-template <typename T>
-class TypeInfo<std::unordered_map<std::string, T>> {
-public:
-	/**
-	 * Put all values from the map as properties to the object at the top of the stack.
-	 *
-	 * @param ctx the context
-	 * @param map the values
-	 * @note You need an object at the top of the stack before calling this function
-	 */
-	static void push(Context &ctx, const std::unordered_map<std::string, T> &map)
-	{
-		for (const auto &pair : map) {
-			TypeInfo<T>::push(ctx, pair.second);
-			duk_put_prop_string(ctx, -2, pair.first.c_str());
-		}
-	}
-};
-
-/**
- * @brief Push or get vectors as JavaScript arrays.
- *
- * Provides: get, push.
- */
-template <typename T>
-class TypeInfo<std::vector<T>> {
-public:
-	/**
-	 * Get an array from the stack.
-	 *
-	 * @param ctx the context
-	 * @param index the array index
-	 * @return the array or empty array if the value is not an array
-	 */
-	static std::vector<T> get(Context &ctx, int index)
-	{
-		std::vector<T> result;
-
-		if (!duk_is_array(ctx, -1))
-			return result;
-
-		int total = duk_get_length(ctx, index);
-		for (int i = 0; i < total; ++i)
-			result.push_back(ctx.getProperty<T>(index, i));
-
-		return result;
-	}
-
-	/**
-	 * Create an array with the specified values.
-	 *
-	 * @param ctx the context
-	 * @param array the values
-	 */
-	static void push(Context &ctx, const std::vector<T> &array)
-	{
-		duk_push_array(ctx);
-
-		int i = 0;
-		for (const auto &v : array) {
-			TypeInfo<T>::push(ctx, v);
-			duk_put_prop_index(ctx, -2, i++);
-		}
-	}
-};
-
-/**
- * @brief Implementation of managed shared_ptr
- * @see Shared
- */
-template <typename T>
-class TypeInfo<Shared<T>> {
-private:
-	static void apply(Context &ctx, std::shared_ptr<T> value)
-	{
-		duk_push_boolean(ctx, false);
-		duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
-		duk_push_pointer(ctx, new std::shared_ptr<T>(value));
-		duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr");
-		duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
-			duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-
-			if (!duk_to_boolean(ctx, -1)) {
-				duk_push_boolean(ctx, true);
-				duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-				duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr");
-				delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
-				duk_pop(ctx);
-			}
-
-			duk_pop(ctx);
-
-			return 0;
-		}, 1);
-		duk_set_finalizer(ctx, -2);
-	}
-
-public:
-	/**
-	 * Construct the shared_ptr as this.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static void construct(Context &ctx, Shared<T> value)
-	{
-		duk_push_this(ctx);
-		apply(ctx, std::move(value.object));
-		duk_pop(ctx);
-	}
-
-	/**
-	 * Push a managed shared_ptr as object.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static void push(Context &ctx, Shared<T> value)
-	{
-		js::StackAssert sa(ctx, 1);
-
-		duk_push_object(ctx);
-		value.object->prototype(ctx);
-		duk_set_prototype(ctx, -2);
-		apply(ctx, value.object);
-	}
-
-	/**
-	 * Get a managed shared_ptr from the stack.
-	 *
-	 * @param ctx the context
-	 * @param index the object index
-	 * @return the shared_ptr
-	 */
-	static std::shared_ptr<T> get(Context &ctx, int index)
-	{
-		/* Verify that it is the correct type */
-		duk_get_prop_string(ctx, index, T::name());
-
-		if (duk_get_type(ctx, -1) == DUK_TYPE_UNDEFINED) {
-			duk_pop(ctx);
-			ctx.raise(ReferenceError("invalid this binding"));
-		} else {
-			duk_pop(ctx);
-		}
-
-		duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr");
-		std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
-		duk_pop(ctx);
-
-		return value;
-	}
-};
-
-/**
- * @brief Implementation of managed pointers
- * @see Pointer
- */
-template <typename T>
-class TypeInfo<Pointer<T>> {
-private:
-	static void apply(Context &ctx, T *value)
-	{
-		duk_push_boolean(ctx, false);
-		duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
-		duk_push_pointer(ctx, value);
-		duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr");
-		duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
-			duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-
-			if (!duk_to_boolean(ctx, -1)) {
-				duk_push_boolean(ctx, true);
-				duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
-				duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr");
-				delete static_cast<T *>(duk_to_pointer(ctx, -1));
-				duk_pop(ctx);
-			}
-
-			duk_pop(ctx);
-
-			return 0;
-		}, 1);
-		duk_set_finalizer(ctx, -2);
-	}
-
-public:
-	/**
-	 * Construct the pointer as this.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static void construct(Context &ctx, Pointer<T> value)
-	{
-		duk_push_this(ctx);
-		apply(ctx, value.object);
-		duk_pop(ctx);
-	}
-
-	/**
-	 * Push a managed pointer as object.
-	 *
-	 * @param ctx the context
-	 * @param value the value
-	 */
-	static void push(Context &ctx, Pointer<T> value)
-	{
-		js::StackAssert sa(ctx, 1);
-
-		duk_push_object(ctx);
-		apply(ctx, value.object);
-		value.object->prototype(ctx);
-		duk_set_prototype(ctx, -2);
-	}
-
-	/**
-	 * Get a managed pointer from the stack.
-	 *
-	 * @param ctx the context
-	 * @param index the object index
-	 * @return the pointer
-	 * @warning Do not store the pointer into the C++ side, the object can be deleted at any time
-	 */
-	static T *get(Context &ctx, int index)
-	{
-		/* Verify that it is the correct type */
-		duk_get_prop_string(ctx, index, T::name());
-
-		if (duk_get_type(ctx, -1) == DUK_TYPE_UNDEFINED) {
-			duk_pop(ctx);
-			ctx.raise(ReferenceError("invalid this binding"));
-		} else {
-			duk_pop(ctx);
-		}
-
-		duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr");
-		T *value = static_cast<T *>(duk_to_pointer(ctx, -1));
-		duk_pop(ctx);
-
-		return value;
-	}
-};
-
-} // !js
-
-} // !irccd
-
-#endif // !_JS_H_
--- a/lib/irccd/js/logger.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/*
- * js-logger.h -- Irccd.Logger API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/js/logger.h>
-#include <irccd/logger.h>
-
-namespace irccd {
-
-namespace {
-
-int print(js::Context &ctx, std::ostream &out)
-{
-	/*
-	 * Get the message before we start printing stuff to avoid
-	 * empty lines.
-	 */
-	out << "plugin ";
-	out << ctx.getGlobal<std::string>("\xff""\xff""name") << ": ";
-	out << ctx.get<std::string>(0) << std::endl;
-
-	return 0;
-}
-
-/*
- * Function: Irccd.Logger.info(message)
- * --------------------------------------------------------
- *
- * Write a verbose message.
- *
- * Arguments:
- *   - message, the message.
- */
-int info(js::Context &ctx)
-{
-	return print(ctx, log::info());
-}
-
-/*
- * Function: Irccd.Logger.warning(message)
- * --------------------------------------------------------
- *
- * Write a warning message.
- *
- * Arguments:
- *   - message, the warning.
- */
-int warning(js::Context &ctx)
-{
-	return print(ctx, log::warning());
-}
-
-/*
- * Function: Logger.debug(message)
- * --------------------------------------------------------
- *
- * Write a debug message, only shown if irccd is compiled in debug.
- *
- * Arguments:
- *   - message, the message.
- */
-int debug(js::Context &ctx)
-{
-	return print(ctx, log::debug());
-}
-
-const js::FunctionMap functions{
-	{ "info",	{ info,		1 } },
-	{ "warning",	{ warning,	1 } },
-	{ "debug",	{ debug,	1 } },
-};
-
-} // !namespace
-
-void loadJsLogger(js::Context &ctx)
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Object{});
-	ctx.push(functions);
-	ctx.putProperty(-2, "Logger");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/logger.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * js-logger.h -- Irccd.Logger API
- *
- * 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_JS_LOGGER_H_
-#define _IRCCD_JS_LOGGER_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsLogger(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_LOGGER_H_
--- a/lib/irccd/js/plugin.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/*
- * js-plugin.cpp -- Irccd.Plugin API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/irccd.h>
-#include <irccd/js/plugin.h>
-
-namespace irccd {
-
-namespace {
-
-/*
- * Wrap function for these functions because they all takes the same arguments.
- *
- * - load,
- * - reload,
- * - unload.
- */
-template <typename Func>
-int wrap(js::Context &ctx, int nret, Func &&func)
-{
-	std::string name = ctx.require<std::string>(0);
-
-	try {
-		func(*ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd"), name);
-	} catch (const std::out_of_range &ex) {
-		ctx.raise(js::ReferenceError(ex.what()));
-	} catch (const std::exception &ex) {
-		ctx.raise(js::Error(ex.what()));
-	}
-
-	return nret;
-}
-
-/*
- * Function: Irccd.Plugin.info([name])
- * ------------------------------------------------------------------
- *
- * Get information about a plugin.
- *
- * The returned object as the following properties:
- *
- * - name: (string) the plugin identifier,
- * - author: (string) the author,
- * - license: (string) the license,
- * - summary: (string) a short description,
- * - version: (string) the version
- *
- * Arguments:
- *   - name, the plugin identifier, if not specified the current plugin is selected.
- * Returns:
- *   The plugin information or undefined if the plugin was not found.
- */
-int info(js::Context &ctx)
-{
-	if (ctx.top() >= 1) {
-		try {
-			ctx.push(ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->requirePlugin(ctx.require<std::string>(0))->info());
-		} catch (...) {
-			ctx.push(js::Undefined{});
-		}
-	} else {
-		ctx.push(ctx.getGlobal<js::RawPointer<Plugin>>("\xff""\xff""plugin")->info());
-	}
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Plugin.list()
- * ------------------------------------------------------------------
- *
- * Get the list of plugins, the array returned contains all plugin names.
- *
- * Returns:
- *   The list of all plugin names.
- */
-int list(js::Context &ctx)
-{
-	ctx.push(js::Array{});
-
-	int i = 0;
-	for (const auto &pair : ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->plugins())
-		ctx.putProperty(-1, i++, pair.first);
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Plugin.load(name)
- * ------------------------------------------------------------------
- *
- * Load a plugin by name. This function will search through the standard directories.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-int load(js::Context &ctx)
-{
-	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-		irccd.loadPlugin(name, name, true);
-	});
-}
-
-/*
- * Function: Irccd.Plugin.reload(name)
- * ------------------------------------------------------------------
- *
- * Reload a plugin by name.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-int reload(js::Context &ctx)
-{
-	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-		irccd.reloadPlugin(name);
-	});
-}
-
-/*
- * Function: Irccd.Plugin.unload(name)
- * ------------------------------------------------------------------
- *
- * Unload a plugin by name.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-int unload(js::Context &ctx)
-{
-	return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-		irccd.unloadPlugin(name);
-	});
-}
-
-const js::FunctionMap functions{
-	{ "info",	{ info,		DUK_VARARGS	} },
-	{ "list",	{ list,		0		} },
-	{ "load",	{ load,		1		} },
-	{ "reload",	{ reload,	1		} },
-	{ "unload",	{ unload,	1		} }
-};
-
-} // !namespace
-
-void loadJsPlugin(js::Context &ctx) noexcept
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Object{});
-	ctx.push(functions);
-	ctx.push(js::Object{});
-	ctx.putProperty(-2, "config");
-	ctx.putProperty(-2, "Plugin");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/plugin.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * js-plugin.h -- Irccd.Plugin API
- *
- * 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_JS_PLUGIN_H_
-#define _IRCCD_JS_PLUGIN_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsPlugin(js::Context &ctx) noexcept;
-
-} // !irccd
-
-#endif // !_IRCCD_JS_TIMER_H_
-
--- a/lib/irccd/js/server.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,540 +0,0 @@
-/*
- * js-server.cpp -- Irccd.Server API
- *
- * 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 <sstream>
-#include <unordered_map>
-
-#include <irccd/irccd.h>
-#include <irccd/server.h>
-
-#include <irccd/js/js.h>
-
-namespace irccd {
-
-namespace {
-
-/*
- * Method: Server.cmode(channel, mode)
- * ------------------------------------------------------------------
- *
- * Change a channel mode.
- *
- * Arguments:
- *   - channel, the channel,
- *   - mode, the mode.
- */
-int cmode(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->cmode(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.cnotice(channel, message)
- * ------------------------------------------------------------------
- *
- * Send a channel notice.
- *
- * Arguments:
- *   - channel, the channel,
- *   - message, the message.
- */
-int cnotice(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->cnotice(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.info()
- * ------------------------------------------------------------------
- *
- * Get the server information as an object containing the following properties:
- *
- * name: the server unique name
- * host: the host name
- * port: the port number
- * ssl: true if using ssl
- * sslVerify: true if ssl was verified
- * channels: an array of all channels
- */
-int info(js::Context &ctx)
-{
-	auto server = ctx.self<js::Shared<Server>>();
-
-	ctx.push(js::Object{});
-	ctx.putProperty(-1, "name", server->info().name);
-	ctx.putProperty(-1, "host", server->info().host);
-	ctx.putProperty<int>(-1, "port", server->info().port);
-	ctx.putProperty<bool>(-1, "ssl", server->info().flags & ServerInfo::Ssl);
-	ctx.putProperty<bool>(-1, "sslVerify", server->info().flags & ServerInfo::SslVerify);
-	ctx.putProperty(-1, "commandChar", server->settings().command);
-	ctx.putProperty(-1, "realname", server->identity().realname);
-	ctx.putProperty(-1, "nickname", server->identity().nickname);
-	ctx.putProperty(-1, "username", server->identity().username);
-
-	/* Channels */
-	ctx.push(js::Array{});
-
-	int i = 0;
-	for (const auto &channel : server->settings().channels)
-		ctx.putProperty(-1, i++, channel.name);
-
-	ctx.putProperty(-2, "channels");
-
-	return 1;
-}
-
-/*
- * Method: Server.invite(target, channel)
- * ------------------------------------------------------------------
- *
- * Invite someone to a channel.
- *
- * Arguments:
- *   - target, the target to invite,
- *   - channel, the channel.
- */
-int invite(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->invite(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.join(channel, password = undefined)
- * ------------------------------------------------------------------
- *
- * Join a channel with an optional password.
- *
- * Arguments:
- *   - channel, the channel to join,
- *   - password, the password or undefined to not use.
- */
-int join(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->join(ctx.require<std::string>(0), ctx.optional<std::string>(1, ""));
-
-	return 0;
-}
-
-/*
- * Method: Server.kick(target, channel, reason = undefined)
- * ------------------------------------------------------------------
- *
- * Kick someone from a channel.
- *
- * Arguments:
- *   - target, the target to kick,
- *   - channel, the channel,
- *   - reason, the optional reason or undefined to not set.
- */
-int kick(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->kick(
-		ctx.require<std::string>(0),
-		ctx.require<std::string>(1),
-		ctx.optional<std::string>(2, "")
-	);
-
-	return 0;
-}
-
-/*
- * Method: Server.me(target, message)
- * ------------------------------------------------------------------
- *
- * Send a CTCP Action.
- *
- * Arguments:
- *   - target, the target or a channel,
- *   - message, the message.
- */
-int me(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->me(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.message(target, message)
- * ------------------------------------------------------------------
- *
- * Send a message.
- *
- * Arguments:
- *   - target, the target or a channel,
- *   - message, the message.
- */
-int message(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->message(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.mode(mode)
- * ------------------------------------------------------------------
- *
- * Change your mode.
- *
- * Arguments:
- *   - mode, the new mode.
- */
-int mode(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->mode(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-/*
- * Method: Server.names(channel)
- * ------------------------------------------------------------------
- *
- * Get the list of names from a channel.
- *
- * Arguments:
- *   - channel, the channel.
- */
-int names(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->names(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-/*
- * Method: Server.nick(nickname)
- * ------------------------------------------------------------------
- *
- * Change the nickname.
- *
- * Arguments:
- *   - nickname, the nickname.
- */
-int nick(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->nick(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-/*
- * Method: Server.notice(target, message)
- * ------------------------------------------------------------------
- *
- * Send a private notice.
- *
- * Arguments:
- *   - target, the target,
- *   - message, the notice message.
- */
-int notice(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->notice(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.part(channel, reason = undefined)
- * ------------------------------------------------------------------
- *
- * Leave a channel.
- *
- * Arguments:
- *   - channel, the channel to leave,
- *   - reason, the optional reason, keep undefined for portability.
- */
-int part(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->part(ctx.require<std::string>(0), ctx.optional<std::string>(1, ""));
-
-	return 0;
-}
-
-/*
- * Method: Server.send(raw)
- * ------------------------------------------------------------------
- *
- * Send a raw message to the IRC server.
- *
- * Arguments:
- *   - raw, the raw message (without terminators).
- */
-int send(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->send(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-/*
- * Method: Server.topic(channel, topic)
- * ------------------------------------------------------------------
- *
- * Change a channel topic.
- *
- * Arguments:
- *   - channel, the channel,
- *   - topic, the new topic.
- */
-int topic(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->topic(ctx.require<std::string>(0), ctx.require<std::string>(1));
-
-	return 0;
-}
-
-/*
- * Method: Server.whois(target)
- * ------------------------------------------------------------------
- *
- * Get whois information.
- *
- * Arguments:
- *   - target, the target.
- */
-int whois(js::Context &ctx)
-{
-	ctx.self<js::Shared<Server>>()->whois(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-/*
- * Method: Server.toString()
- * ------------------------------------------------------------------
- *
- * Convert the object to std::string, convenience for adding the object
- * as property key.
- *
- * Returns:
- *   The server name (unique).
- */
-int toString(js::Context &ctx)
-{
-	ctx.push(ctx.self<js::Shared<Server>>()->info().name);
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Server(params) [constructor]
- * ------------------------------------------------------------------
- *
- * Construct a new server.
- *
- * Params must be filled with the following properties:
- *
- * name: the name,
- * host: the host,
- * ipv6: true to use ipv6,	(Optional: default false)
- * port: the port number,	(Optional: default 6667)
- * password: the password,	(Optional: default none)
- * channels: array of channels	(Optiona: default empty)
- * ssl: true to use ssl,	(Optional: default false)
- * sslVerify: true to verify	(Optional: default true)
- * nickname: "nickname",	(Optional, default: irccd)
- * username: "user name",	(Optional, default: irccd)
- * realname: "real name",	(Optional, default: IRC Client Daemon)
- * commandChar: "!",		(Optional, the command char, default: "!")
- */
-int constructor(js::Context &ctx)
-{
-	if (!duk_is_constructor_call(ctx))
-		return 0;
-
-	ServerInfo info;
-	ServerIdentity identity;
-	ServerSettings settings;
-
-	/* Information part */
-	info.name = ctx.getProperty<std::string>(0, "name");
-	info.host = ctx.getProperty<std::string>(0, "host");
-	info.port = ctx.optionalProperty<int>(0, "port", info.port);
-	info.password = ctx.optionalProperty<std::string>(0, "password", "");
-
-	if (ctx.optionalProperty<bool>(0, "ipv6", false))
-		info.flags |= ServerInfo::Ipv6;
-
-	/* Identity part */
-	identity.nickname = ctx.optionalProperty<std::string>(0, "nickname", identity.nickname);
-	identity.username = ctx.optionalProperty<std::string>(0, "username", identity.username);
-	identity.realname = ctx.optionalProperty<std::string>(0, "realname", identity.realname);
-	identity.ctcpversion = ctx.optionalProperty<std::string>(0, "version", identity.ctcpversion);
-
-	/* Settings part */
-	for (const auto &chan: ctx.getProperty<std::vector<std::string>>(0, "channels"))
-		settings.channels.push_back(Server::splitChannel(chan));
-
-	settings.recotries = ctx.optionalProperty<int>(0, "recoTries", settings.recotries);
-	settings.recotimeout = ctx.optionalProperty<int>(0, "recoTimeout", settings.recotimeout);
-
-	if (ctx.optionalProperty<bool>(0, "joinInvite", false))
-		settings.flags |= ServerSettings::JoinInvite;
-	if (ctx.optionalProperty<bool>(0, "autoRejoin", false))
-		settings.flags |= ServerSettings::AutoRejoin;
-
-	try {
-		ctx.construct(js::Shared<Server>{std::make_shared<Server>(std::move(info), std::move(identity), std::move(settings))});
-	} catch (const std::exception &ex) {
-		ctx.raise(js::Error(ex.what()));
-	}
-
-	return 0;
-}
-
-/*
- * Function: Irccd.Server.add(s)
- * ------------------------------------------------------------------
- *
- * Register a new server to the irccd instance.
- *
- * Arguments:
- *   - s, the server to add.
- */
-int add(js::Context &ctx)
-{
-	std::shared_ptr<Server> server = ctx.get<js::Shared<Server>>(0);
-
-	if (server)
-		ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->addServer(server);
-
-	return 0;
-}
-
-/*
- * Function: Irccd.Server.find(name)
- * ------------------------------------------------------------------
- *
- * Find a server by name.
- *
- * Arguments:
- *   - name, the server name
- * Returns:
- *   The server object or undefined if not found.
- */
-int find(js::Context &ctx)
-{
-	const auto name = ctx.require<std::string>(0);
-	const auto irccd = ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd");
-
-	try {
-		ctx.push(js::Shared<Server>{irccd->requireServer(name)});
-	} catch (...) {
-		return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Server.list()
- * ------------------------------------------------------------------
- *
- * Get the map of all loaded servers.
- *
- * Returns:
- *   An object with string-to-servers pairs.
- */
-int list(js::Context &ctx)
-{
-	ctx.push(js::Object{});
-
-	for (const auto &pair : ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->servers())
-		ctx.putProperty(-1, pair.first, js::Shared<Server>{pair.second});
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Server.remove(name)
- * ------------------------------------------------------------------
- *
- * Remove a server from the irccd instance. You can pass the server object since it's coercible to a string.
- *
- * Arguments:
- *   - name the server name.
- */
-int remove(js::Context &ctx)
-{
-	ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->removeServer(ctx.require<std::string>(0));
-
-	return 0;
-}
-
-const js::FunctionMap methods{
-	{ "cmode",	{ cmode,	2		} },
-	{ "cnotice",	{ cnotice,	2		} },
-	{ "info",	{ info,		0		} },
-	{ "invite",	{ invite,	2		} },
-	{ "join",	{ join,		DUK_VARARGS	} },
-	{ "kick",	{ kick,		DUK_VARARGS	} },
-	{ "me",		{ me,		2		} },
-	{ "message",	{ message,	2		} },
-	{ "mode",	{ mode,		1		} },
-	{ "names",	{ names,	1		} },
-	{ "nick",	{ nick,		1		} },
-	{ "notice",	{ notice,	2		} },
-	{ "part",	{ part,		DUK_VARARGS	} },
-	{ "send",	{ send,		1		} },
-	{ "topic",	{ topic,	2		} },
-	{ "whois",	{ whois,	1		} },
-	{ "toString",	{ toString,	0		} }
-};
-
-const js::FunctionMap functions{
-	{ "add",	{ add,		1		} },
-	{ "find",	{ find,		1		} },
-	{ "list",	{ list,		0		} },
-	{ "remove",	{ remove,	1		} }
-};
-
-} // !namespace
-
-void loadJsServer(js::Context &ctx)
-{
-	/* Server prototype for both constructing and passing */
-	ctx.push(js::Object{});
-	ctx.push(methods);
-	ctx.putGlobal("\xff""\xff""Server-proto");
-
-	/* Server object */
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Function{constructor, 1});
-	ctx.push(functions);
-
-	/* Prototype */
-	ctx.getGlobal<void>("\xff""\xff""Server-proto");
-	ctx.push(methods);
-	ctx.putProperty(-1, "\xff""\xff""Server", true);
-	ctx.putProperty(-2, "prototype");
-
-	/* Put Server */
-	ctx.putProperty(-2, "Server");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/server.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * js-server.h -- Irccd.Server API
- *
- * 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_JS_SERVER_H_
-#define _IRCCD_JS_SERVER_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsServer(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_SERVER_H_
--- a/lib/irccd/js/system.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,236 +0,0 @@
-/*
- * js-system.cpp -- Irccd.System API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd-config.h>
-
-#include <chrono>
-
-#if defined(HAVE_POPEN)
-#  include <cstdio>
-#endif
-
-#include <cstdlib>
-#include <thread>
-
-#include <irccd/system.h>
-
-#include <irccd/js/file.h>
-#include <irccd/js/irccd.h>
-#include <irccd/js/system.h>
-
-namespace irccd {
-
-namespace {
-
-/*
- * Function: Irccd.System.env(key)
- * ------------------------------------------------------------------
- *
- * Get an environment system variable.
- *
- * Arguments:
- *   - key, the environment variable.
- * Returns:
- *   The value.
- */
-int env(js::Context &ctx)
-{
-	ctx.push(sys::env(ctx.get<std::string>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.System.exec(cmd)
- * ------------------------------------------------------------------
- *
- * Execute a system command.
- *
- * Arguments:
- *   - cmd, the command to execute.
- */
-int exec(js::Context &ctx)
-{
-	std::system(ctx.get<const char *>(0));
-
-	return 0;
-}
-
-/*
- * Function: Irccd.System.home()
- * ------------------------------------------------------------------
- *
- * Get the operating system user's home.
- *
- * Returns:
- *   The user home directory.
- */
-int home(js::Context &ctx)
-{
-	ctx.push(sys::home());
-
-	return 1;
-}
-
-/*
- * Function: Irccd.System.name()
- * ------------------------------------------------------------------
- *
- * Get the operating system name.
- *
- * Returns:
- *   The system name.
- */
-int name(js::Context &ctx)
-{
-	ctx.push(sys::name());
-
-	return 1;
-}
-
-#if defined(HAVE_POPEN)
-
-/*
- * Function: Irccd.System.popen(cmd, mode) [optional]
- * ------------------------------------------------------------------
- *
- * Wrapper for popen(3) if the function is available.
- *
- * Arguments:
- *   - cmd, the command to execute,
- *   - mode, the mode (e.g. "r").
- * Returns:
- *   A Irccd.File object.
- * Throws
- *   - Irccd.SystemError on failures.
- */
-int popen(js::Context &ctx)
-{
-	auto fp = ::popen(ctx.require<const char *>(0), ctx.require<const char *>(1));
-
-	if (fp == nullptr)
-		ctx.raise(SystemError{});
-
-	ctx.push(js::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })});
-
-	return 1;
-}
-
-#endif // !HAVE_POPEN
-
-/*
- * Function: Irccd.System.sleep(delay)
- * ------------------------------------------------------------------
- *
- * Sleep the main loop for the specific delay in seconds.
- */
-int sleep(js::Context &ctx)
-{
-	std::this_thread::sleep_for(std::chrono::seconds(ctx.get<int>(0)));
-
-	return 0;
-}
-
-/*
- * Function: Irccd.System.ticks()
- * ------------------------------------------------------------------
- *
- * Get the number of milliseconds since irccd was started.
- *
- * Returns:
- *   The number of milliseconds.
- */
-int ticks(js::Context &ctx)
-{
-	ctx.push(static_cast<int>(sys::ticks()));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.System.usleep(delay)
- * ------------------------------------------------------------------
- *
- * Sleep the main loop for the specific delay in microseconds.
- */
-int usleep(js::Context &ctx)
-{
-	std::this_thread::sleep_for(std::chrono::microseconds(ctx.get<int>(0)));
-
-	return 0;
-}
-
-/*
- * Function: Irccd.System.uptime()
- * ------------------------------------------------------------------
- *
- * Get the system uptime.
- *
- * Returns:
- *   The system uptime.
- */
-int uptime(js::Context &ctx)
-{
-	ctx.push<int>(sys::uptime());
-
-	return 0;
-}
-
-/*
- * Function: Irccd.System.version()
- * ------------------------------------------------------------------
- *
- * Get the operating system version.
- *
- * Returns:
- *   The system version.
- */
-int version(js::Context &ctx)
-{
-	ctx.push(sys::version());
-
-	return 1;
-}
-
-const js::FunctionMap functions{
-	{ "env",	{ env,		1	} },
-	{ "exec",	{ exec,		1	} },
-	{ "home",	{ home,		0	} },
-	{ "name",	{ name,		0	} },
-#if defined(HAVE_POPEN)
-	{ "popen",	{ popen,	2	} }, 
-#endif
-	{ "sleep",	{ sleep,	1	} },
-	{ "ticks",	{ ticks,	0	} },
-	{ "uptime",	{ uptime,	0	} },
-	{ "usleep",	{ usleep,	1	} },
-	{ "version",	{ version,	0	} }
-};
-
-} // !namespace
-
-void loadJsSystem(js::Context &ctx)
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Object{});
-	ctx.push(functions);
-	ctx.putProperty(-2, "System");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/system.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * js-system.h -- Irccd.System API
- *
- * 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_JS_SYSTEM_H_
-#define _IRCCD_JS_SYSTEM_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsSystem(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_SYSTEM_H_
--- a/lib/irccd/js/timer.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * js-timer.cpp -- Irccd.Timer API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <cassert>
-#include <cstdint>
-
-#include <irccd/plugin.h>
-
-#include <irccd/js/js.h>
-
-namespace irccd {
-
-class JsTimer : public Timer {
-public:
-	using Timer::Timer;
-
-	~JsTimer()
-	{
-		stop();
-	}
-
-	static inline const char *name() noexcept
-	{
-		return "\xff""\xff""Timer";
-	}
-};
-
-namespace {
-
-/*
- * Method: Timer.start()
- * --------------------------------------------------------
- *
- * Start the timer. If the timer is already started the method
- * is a no-op.
- */
-int start(js::Context &ctx)
-{
-	auto timer = ctx.self<js::Shared<JsTimer>>();
-
-	if (!timer->isRunning())
-		timer->start();
-
-	return 0;
-}
-
-/*
- * Method: Timer.stop()
- * --------------------------------------------------------
- *
- * Stop the timer.
- */
-int stop(js::Context &ctx)
-{
-	auto timer = ctx.self<js::Shared<JsTimer>>();
-
-	if (timer->isRunning())
-		timer->stop();
-
-	return 0;
-}
-
-const js::FunctionMap methods{
-	{ "start",	{ start,	0 } },
-	{ "stop",	{ stop,		0 } }
-};
-
-/*
- * Function: Irccd.Timer(type, delay, callback) [constructor]
- * --------------------------------------------------------
- *
- * Create a new timer object.
- *
- * Arguments:
- *   - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat),
- *   - delay, the interval in milliseconds,
- *   - callback, the function to call.
- */
-int constructor(js::Context &ctx)
-{
-	int type = ctx.require<int>(0);
-	int delay = ctx.require<int>(1);
-
-	if (!duk_is_callable(ctx, 2))
-		ctx.raise(js::TypeError{"missing callback function"});
-
-	auto timer = std::make_shared<JsTimer>(static_cast<TimerType>(type), delay);
-
-	/* Add this timer to the underlying plugin */
-	ctx.getGlobal<js::RawPointer<Plugin>>("\xff""\xff""plugin")->addTimer(timer);
-
-	/* Construct object */
-	ctx.construct(js::Shared<JsTimer>{timer});
-
-	/* Now store the JavaScript function to call */
-	ctx.dup(2);
-	ctx.putGlobal("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
-
-	return 0;
-}
-
-const js::Map<int> constants{
-	{ "Single",	static_cast<int>(TimerType::Single) },
-	{ "Repeat",	static_cast<int>(TimerType::Repeat) },
-};
-
-} // !namespace
-
-void loadJsTimer(js::Context &ctx) noexcept
-{
-	ctx.getGlobal<void>("Irccd");
-
-	/* Timer object */
-	ctx.push(js::Function{constructor, 3});
-	ctx.push(constants);
-
-	/* Prototype */
-	ctx.push(js::Object{});
-	ctx.push(methods);
-	ctx.putProperty(-1, "\xff""\xff""Timer", true);
-	ctx.putProperty(-2, "prototype");
-
-	/* Put Timer */
-	ctx.putProperty(-2, "Timer");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/timer.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * js-timer.h -- Irccd.Timer API
- *
- * 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_JS_TIMER_H_
-#define _IRCCD_JS_TIMER_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsTimer(js::Context &ctx) noexcept;
-
-} // !irccd
-
-#endif // !_IRCCD_JS_TIMER_H_
-
--- a/lib/irccd/js/unicode.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * js-unicode.cpp -- Irccd.Unicode API
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/unicode.h>
-
-#include "js.h"
-
-namespace irccd {
-
-namespace {
-
-/*
- * Function: Irccd.Unicode.isDigit(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the digit category.
- */
-int isDigit(js::Context &ctx)
-{
-	ctx.push(unicode::isdigit(ctx.get<int>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isLetter(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the letter category.
- */
-int isLetter(js::Context &ctx)
-{
-	ctx.push(unicode::isalpha(ctx.get<int>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isLower(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is lower case.
- */
-int isLower(js::Context &ctx)
-{
-	ctx.push(unicode::islower(ctx.get<int>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isSpace(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the space category.
- */
-int isSpace(js::Context &ctx)
-{
-	ctx.push(unicode::isspace(ctx.get<int>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isTitle(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is title case.
- */
-int isTitle(js::Context &ctx)
-{
-	ctx.push(unicode::istitle(ctx.get<int>(0)));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isUpper(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is upper case.
- */
-int isUpper(js::Context &ctx)
-{
-	ctx.push(unicode::isupper(ctx.get<int>(0)));
-
-	return 1;
-}
-
-const js::FunctionMap functions{
-	{ "isDigit",		{ isDigit,	1	} },
-	{ "isLetter",		{ isLetter,	1	} },
-	{ "isLower",		{ isLower,	1	} },
-	{ "isSpace",		{ isSpace,	1	} },
-	{ "isTitle",		{ isTitle,	1	} },
-	{ "isUpper",		{ isUpper,	1	} },
-};
-
-} // !namespace
-
-void loadJsUnicode(js::Context &ctx)
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Object{});
-	ctx.push(functions);
-	ctx.putProperty(-2, "Unicode");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/unicode.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * js-unicode.cpp -- Irccd.Unicode API
- *
- * 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_JS_UNICODE_H_
-#define _IRCCD_JS_UNICODE_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsUnicode(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_UNICODE_H_
--- a/lib/irccd/js/util.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-/*
- * js-util.cpp -- Irccd.Util API
- *
- * 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 <libircclient.h>
-
-#include <irccd/util.h>
-
-#include <irccd/js/util.h>
-
-namespace irccd {
-
-namespace js {
-
-/**
- * Read parameters for Irccd.Util.format function, the object is defined as follow:
- *
- * {
- *   date: the date object
- *   flags: the flags (not implemented yet)
- *   field1: a field to substitute in #{} pattern
- *   field2: a field to substitute in #{} pattern
- *   fieldn: ...
- * }
- */
-template <>
-class TypeInfo<util::Substitution> {
-public:
-	static util::Substitution get(Context &ctx, int index)
-	{
-		util::Substitution params;
-
-		if (!ctx.is<Object>(index))
-			return params;
-
-		ctx.enumerate(index, 0, true, [&] (Context &) {
-			if (ctx.get<std::string>(-2) == "date")
-				params.time = static_cast<time_t>(ctx.get<double>(-1) / 1000);
-			else
-				params.keywords.insert({ctx.get<std::string>(-2), ctx.get<std::string>(-1)});
-		});
-
-		return params;
-	}
-};
-
-} // !js
-
-namespace {
-
-/*
- * Function: Irccd.Util.format(text, parameters)
- * --------------------------------------------------------
- *
- * Format a string with templates.
- *
- * Arguments:
- *   - input, the text to update,
- *   - params, the parameters.
- * Returns:
- *   The converted text.
- */
-int format(js::Context &ctx)
-{
-	try {
-		ctx.push(util::format(ctx.get<std::string>(0), ctx.get<util::Substitution>(1)));
-	} catch (const std::exception &ex) {
-		ctx.raise(js::SyntaxError(ex.what()));
-	}
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Util.splituser(ident)
- * --------------------------------------------------------
- *
- * Return the nickname part from a full username.
- *
- * Arguments:
- *   - ident, the full identity.
- * Returns:
- *   The nickname.
- */
-int splituser(js::Context &ctx)
-{
-	const char *target = ctx.require<const char *>(0);
-	char nick[32] = {0};
-
-	irc_target_get_nick(target, nick, sizeof (nick) -1);
-	ctx.push(std::string(nick));
-
-	return 1;
-}
-
-/*
- * Function: Irccd.Util.splithost(ident)
- * --------------------------------------------------------
- *
- * Return the hostname part from a full username.
- *
- * Arguments:
- *   - ident, the full identity.
- * Returns:
- *   The hostname.
- */
-int splithost(js::Context &ctx)
-{
-	const char *target = ctx.require<const char *>(0);
-	char host[32] = {0};
-
-	irc_target_get_host(target, host, sizeof (host) -1);
-	ctx.push(std::string(host));
-
-	return 1;
-}
-
-const js::FunctionMap functions{
-	{ "format",		{ format,	DUK_VARARGS	} },
-	{ "splituser",		{ splituser,	1		} },
-	{ "splithost",		{ splithost,	1		} }
-};
-
-} // !namespace
-
-void loadJsUtil(js::Context &ctx)
-{
-	ctx.getGlobal<void>("Irccd");
-	ctx.push(js::Object{});
-	ctx.push(functions);
-	ctx.putProperty(-2, "Util");
-	ctx.pop();
-}
-
-} // !irccd
--- a/lib/irccd/js/util.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * js-util.h -- Irccd.Util API
- *
- * 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_JS_UTIL_H_
-#define _IRCCD_JS_UTIL_H_
-
-#include "js.h"
-
-namespace irccd {
-
-void loadJsUtil(js::Context &ctx);
-
-} // !irccd
-
-#endif // !_IRCCD_JS_UTIL_H_
--- a/lib/irccd/path.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/path.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -53,10 +53,10 @@
 #    include <libproc.h>
 #  endif
 
-#  include "private/xdg.h"
+#  include "xdg.h"
 #endif
 
-#include "private/filesystem.h"
+#include "filesystem.h"
 #include "path.h"
 #include "system.h"
 #include "util.h"
--- a/lib/irccd/plugin.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/plugin.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -26,24 +26,22 @@
 #  include <cstring>
 #endif
 
-#include "private/filesystem.h"
+#include "filesystem.h"
+#include "js-directory.h"
+#include "js-elapsed-timer.h"
+#include "js-file.h"
+#include "js-irccd.h"
+#include "js-logger.h"
+#include "js-plugin.h"
+#include "js-server.h"
+#include "js-system.h"
+#include "js-timer.h"
+#include "js-unicode.h"
+#include "js-util.h"
 #include "path.h"
-#include "util.h"
-
-#include "js/directory.h"
-#include "js/elapsed-timer.h"
-#include "js/file.h"
-#include "js/irccd.h"
-#include "js/logger.h"
-#include "js/plugin.h"
-#include "js/server.h"
-#include "js/system.h"
-#include "js/timer.h"
-#include "js/unicode.h"
-#include "js/util.h"
-
 #include "plugin.h"
 #include "server.h"
+#include "util.h"
 
 using namespace std;
 
@@ -51,33 +49,33 @@
 
 void Plugin::call(const string &name, unsigned nargs)
 {
-	m_context.getGlobal<void>(name);
+	duk::getGlobal<void>(m_context, name);
 
-	if (m_context.type(-1) == DUK_TYPE_UNDEFINED) {
+	if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED) {
 		/* Function not defined, remove the undefined value and all arguments */
-		m_context.pop(nargs + 1);
+		duk::pop(m_context, nargs + 1);
 	} else {
 		/* Call the function and discard the result */
-		m_context.insert(-nargs - 1);
-		m_context.pcall(nargs);
-		m_context.pop();
+		duk::insert(m_context, -nargs - 1);
+		duk::pcall(m_context, nargs);
+		duk::pop(m_context);
 	}
 }
 
 void Plugin::putVars()
 {
-	js::StackAssert sa{m_context};
+	duk::StackAssert sa{m_context};
 
 	/* Save a reference to this */
-	m_context.putGlobal("\xff""\xff""plugin", js::RawPointer<Plugin>{this});
-	m_context.putGlobal("\xff""\xff""name", m_info.name);
-	m_context.putGlobal("\xff""\xff""path", m_info.path);
-	m_context.putGlobal("\xff""\xff""parent", m_info.parent);
+	duk::putGlobal(m_context, "\xff""\xff""plugin", duk::RawPointer<Plugin>{this});
+	duk::putGlobal(m_context, "\xff""\xff""name", m_info.name);
+	duk::putGlobal(m_context, "\xff""\xff""path", m_info.path);
+	duk::putGlobal(m_context, "\xff""\xff""parent", m_info.parent);
 }
 
 void Plugin::putPath(const std::string &varname, const std::string &append, path::Path type)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	bool found = true;
 	std::string foundpath;
@@ -98,15 +96,15 @@
 	if (!found)
 		foundpath = path::clean(path::get(type, path::OwnerSystem) + append);
 
-	m_context.getGlobal<void>("Irccd");
-	m_context.getProperty<void>(-1, "Plugin");
-	m_context.putProperty(-1, varname, foundpath);
-	m_context.pop(2);
+	duk::getGlobal<void>(m_context, "Irccd");
+	duk::getProperty<void>(m_context, -1, "Plugin");
+	duk::putProperty(m_context, -1, varname, foundpath);
+	duk::pop(m_context, 2);
 }
 
 void Plugin::putPaths()
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	/*
 	 * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/)
@@ -119,28 +117,30 @@
 
 void Plugin::putConfig(const PluginConfig &config)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	// TODO: override dataPath, configPath, cachePath
 
 	/* Store plugin configuration into Irccd.Plugin.config */
-	m_context.getGlobal<void>("Irccd");
-	m_context.getProperty<void>(-1, "Plugin");
-	m_context.getProperty<void>(-1, "config");
+	duk::getGlobal<void>(m_context, "Irccd");
+	duk::getProperty<void>(m_context, -1, "Plugin");
+	duk::getProperty<void>(m_context, -1, "config");
 
-	if (m_context.type(-1) != DUK_TYPE_OBJECT) {
-		m_context.pop();
-		m_context.push(js::Object{});
+	if (duk::type(m_context, -1) != DUK_TYPE_OBJECT) {
+		duk::pop(m_context);
+		duk::push(m_context, duk::Object{});
 	}
 
-	m_context.push(config);
-	m_context.putProperty(-2, "config");
-	m_context.pop(2);
+	for (const auto &pair : config)
+		duk::putProperty(m_context, -1, pair.first, pair.second);
+
+	duk::putProperty(m_context, -2, "config");
+	duk::pop(m_context, 2);
 }
 
 Plugin::Plugin(std::string name, std::string path, const PluginConfig &config)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	m_info.name = std::move(name);
 	m_info.path = std::move(path);
@@ -186,23 +186,25 @@
 	putPaths();
 
 	/* Try to load the file (does not call onLoad yet) */
-	m_context.peval(js::File{m_info.path});
-	m_context.pop();
+	if (duk::pevalFile(m_context, m_info.path) != 0)
+		throw duk::error(m_context, -1);
+
+	duk::pop(m_context);
 
 	/* Initialize user defined options after loading to allow the plugin to define default values */
 	putConfig(config);
 
 	/* Read metadata */
-	m_context.getGlobal<void>("info");
+	duk::getGlobal<void>(m_context, "info");
 
-	if (m_context.type(-1) == DUK_TYPE_OBJECT) {
-		m_info.author = m_context.optionalProperty<std::string>(-1, "author", "unknown");
-		m_info.license = m_context.optionalProperty<std::string>(-1, "license", "unknown");
-		m_info.summary = m_context.optionalProperty<std::string>(-1, "summary", "unknown");
-		m_info.version = m_context.optionalProperty<std::string>(-1, "version", "unknown");
+	if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) {
+		m_info.author = duk::optionalProperty<std::string>(m_context, -1, "author", "unknown");
+		m_info.license = duk::optionalProperty<std::string>(m_context, -1, "license", "unknown");
+		m_info.summary = duk::optionalProperty<std::string>(m_context, -1, "summary", "unknown");
+		m_info.version = duk::optionalProperty<std::string>(m_context, -1, "version", "unknown");
 	}
 
-	m_context.pop();
+	duk::pop(m_context);
 
 	log::debug() << "plugin " << m_info.name << ": " << std::endl;
 	log::debug() << "  author:  " << m_info.author << std::endl;
@@ -211,6 +213,12 @@
 	log::debug() << "  version: " << m_info.version << std::endl;
 }
 
+Plugin::~Plugin()
+{
+	for (auto &timer : m_timers)
+		timer->stop();
+}
+
 const PluginInfo &Plugin::info() const
 {
 	return m_info;
@@ -242,9 +250,11 @@
 
 void Plugin::removeTimer(const std::shared_ptr<Timer> &timer) noexcept
 {
+	duk::StackAssert sa(m_context);
+
 	/* Remove the JavaScript function */
-	m_context.push(js::Null{});
-	m_context.putGlobal("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
+	duk::push(m_context, duk::Null{});
+	duk::putGlobal(m_context, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get())));
 
 	/* Remove from list */
 	m_timers.erase(timer);
@@ -252,229 +262,229 @@
 
 void Plugin::onChannelMode(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(mode));
-	m_context.push(move(arg));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(mode));
+	duk::push(m_context, move(arg));
 	call("onChannelMode", 5);
 }
 
 void Plugin::onChannelNotice(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string notice)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(notice));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(notice));
 	call("onChannelNotice", 4);
 }
 
 void Plugin::onCommand(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(message));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(message));
 	call("onCommand", 4);
 }
 
 void Plugin::onConnect(std::shared_ptr<Server> server)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
+	duk::push(m_context, duk::Shared<Server>{server});
 	call("onConnect", 1);
 }
 
 void Plugin::onInvite(std::shared_ptr<Server> server, std::string origin, std::string channel)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
 	call("onInvite", 3);
 }
 
 void Plugin::onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
 	call("onJoin", 3);
 }
 
 void Plugin::onKick(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(target));
-	m_context.push(move(reason));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(target));
+	duk::push(m_context, move(reason));
 	call("onKick", 5);
 }
 
 void Plugin::onLoad()
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	call("onLoad", 0);
 }
 
 void Plugin::onMessage(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(message));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(message));
 	call("onMessage", 4);
 }
 
 void Plugin::onMe(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(message));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(message));
 	call("onMe", 4);
 }
 
 void Plugin::onMode(std::shared_ptr<Server> server, std::string origin, std::string mode)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(mode));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(mode));
 	call("onMode", 3);
 }
 
 void Plugin::onNames(std::shared_ptr<Server> server, std::string channel, std::vector<std::string> names)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(channel));
-	m_context.push(move(names));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(names));
 	call("onNames", 3);
 }
 
 void Plugin::onNick(std::shared_ptr<Server> server, std::string oldnick, std::string newnick)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(oldnick));
-	m_context.push(move(newnick));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(oldnick));
+	duk::push(m_context, move(newnick));
 	call("onNick", 3);
 }
 
 void Plugin::onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(notice));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(notice));
 	call("onNotice", 3);
 }
 
 void Plugin::onPart(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string reason)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(reason));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(reason));
 	call("onPart", 4);
 }
 
 void Plugin::onQuery(std::shared_ptr<Server> server, std::string origin, std::string message)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(message));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(message));
 	call("onQuery", 3);
 }
 
 void Plugin::onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(message));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(message));
 	call("onQueryCommand", 3);
 }
 
 void Plugin::onReload()
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	call("onReload");
 }
 
 void Plugin::onTopic(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string topic)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(topic));
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, move(origin));
+	duk::push(m_context, move(channel));
+	duk::push(m_context, move(topic));
 	call("onTopic", 4);
 }
 
 void Plugin::onUnload()
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
 	call("onUnload");
 }
 
 void Plugin::onWhois(std::shared_ptr<Server> server, ServerWhois whois)
 {
-	js::StackAssert sa(m_context);
+	duk::StackAssert sa(m_context);
 
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(js::Object{});
-	m_context.putProperty(-1, "nickname", whois.nick);
-	m_context.putProperty(-1, "username", whois.user);
-	m_context.putProperty(-1, "realname", whois.realname);
-	m_context.putProperty(-1, "host", whois.host);
-	m_context.putProperty(1, "channels", whois.channels);
+	duk::push(m_context, duk::Shared<Server>{server});
+	duk::push(m_context, duk::Object{});
+	duk::putProperty(m_context, -1, "nickname", whois.nick);
+	duk::putProperty(m_context, -1, "username", whois.user);
+	duk::putProperty(m_context, -1, "realname", whois.realname);
+	duk::putProperty(m_context, -1, "host", whois.host);
+	duk::putProperty(m_context, 1, "channels", whois.channels);
 	call("onWhois", 2);
 }
 
-namespace js {
+namespace duk {
 
-void TypeInfo<PluginInfo>::push(Context &ctx, const PluginInfo &info)
+void TypeTraits<irccd::PluginInfo>::push(ContextPtr ctx, const PluginInfo &info)
 {
-	js::StackAssert sa(ctx, 1);
+	duk::StackAssert sa(ctx, 1);
 
-	ctx.push(js::Object{});
-	ctx.putProperty(-1, "name", info.name);
-	ctx.putProperty(-1, "author", info.author);
-	ctx.putProperty(-1, "license", info.license);
-	ctx.putProperty(-1, "summary", info.summary);
-	ctx.putProperty(-1, "version", info.version);
+	duk::push(ctx, duk::Object{});
+	duk::putProperty(ctx, -1, "name", info.name);
+	duk::putProperty(ctx, -1, "author", info.author);
+	duk::putProperty(ctx, -1, "license", info.license);
+	duk::putProperty(ctx, -1, "summary", info.summary);
+	duk::putProperty(ctx, -1, "version", info.version);
 }
 
 } // !js
--- a/lib/irccd/plugin.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/plugin.h	Wed Mar 30 13:52:47 2016 +0200
@@ -30,11 +30,9 @@
 #include <unordered_set>
 #include <vector>
 
-#include <irccd/path.h>
-#include <irccd/private/signals.h>
-
-#include "js/js.h"
-
+#include "js.h"
+#include "path.h"
+#include "signals.h"
 #include "timer.h"
 
 namespace irccd {
@@ -102,7 +100,7 @@
 
 private:
 	/* JavaScript context */
-	js::Context m_context;
+	duk::Context m_context;
 
 	/* Plugin info and its timers */
 	PluginInfo m_info;
@@ -127,6 +125,11 @@
 	Plugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
 
 	/**
+	 * Temporary, close all timers.
+	 */
+	~Plugin();
+
+	/**
 	 * Get the plugin information.
 	 */
 	const PluginInfo &info() const;
@@ -150,7 +153,7 @@
 	 *
 	 * @return the context
 	 */
-	inline js::Context &context() noexcept
+	inline duk::Context &context() noexcept
 	{
 		return m_context;
 	}
@@ -342,15 +345,15 @@
 	void onWhois(std::shared_ptr<Server> server, ServerWhois info);
 };
 
-namespace js {
+namespace duk {
 
 template <>
-class TypeInfo<PluginInfo> {
+class TypeTraits<irccd::PluginInfo> {
 public:
-	static void push(Context &ctx, const PluginInfo &info);
+	static void push(ContextPtr ctx, const irccd::PluginInfo &info);
 };
 
-} // !js
+} // !duk
 
 } // !irccd
 
--- a/lib/irccd/private/CMakeSources.cmake	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-set(
-	PRIVATE_HEADERS
-	${CMAKE_CURRENT_LIST_DIR}/connection.h
-	${CMAKE_CURRENT_LIST_DIR}/directory.h
-	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h
-	${CMAKE_CURRENT_LIST_DIR}/ini.h
-	${CMAKE_CURRENT_LIST_DIR}/filesystem.h
-	${CMAKE_CURRENT_LIST_DIR}/sockets.h
-	${CMAKE_CURRENT_LIST_DIR}/xdg.h
-)
-
-set(
-	PRIVATE_SOURCES
-	${CMAKE_CURRENT_LIST_DIR}/connection.cpp
-	${CMAKE_CURRENT_LIST_DIR}/directory.cpp
-	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp
-	${CMAKE_CURRENT_LIST_DIR}/ini.cpp
-	${CMAKE_CURRENT_LIST_DIR}/filesystem.cpp
-	${CMAKE_CURRENT_LIST_DIR}/sockets.cpp
-	${CMAKE_CURRENT_LIST_DIR}/xdg.cpp
-)
--- a/lib/irccd/private/connection.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * connection.cpp -- value wrapper for connecting to irccd
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <irccd/logger.h>
-
-#include "connection.h"
-
-namespace irccd {
-
-json::Value Connection::next(const std::string &name, int timeout)
-{
-	m_timer.reset();
-
-	while (isConnected()) {
-		json::Value object = next(clamp(timeout));
-
-		if (object.isObject() && object["response"].toString() == name)
-			return object;
-	}
-
-	throw std::runtime_error("connection lost");
-}
-
-void Connection::verify(const std::string &name, int timeout)
-{
-	auto object = next(name, timeout);
-	auto value = object.at("status").toString();
-
-	if (!value.empty() && value != "ok")
-		throw std::runtime_error(object.at("error").toString());
-}
-
-} // !irccd
--- a/lib/irccd/private/connection.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * connection.h -- value wrapper for connecting to irccd
- *
- * 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_CONNECTION_H_
-#define _IRCCD_CONNECTION_H_
-
-#include <cassert>
-#include <stdexcept>
-
-#include <irccd/json.h>
-#include <irccd/system.h>
-#include <irccd/util.h>
-
-#include "elapsed-timer.h"
-#include "sockets.h"
-
-namespace irccd {
-
-/**
- * @class Connection
- * @brief Abstract class for connecting to irccd from Ip or Local addresses.
- */
-class Connection {
-protected:
-	ElapsedTimer m_timer;
-
-	/**
-	 * Clamp the time to wait to be sure that it will be never less than 0.
-	 */
-	inline int clamp(int timeout) noexcept
-	{
-		return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed());
-	}
-
-public:
-	/**
-	 * Default constructor.
-	 */
-	Connection() = default;
-
-	/**
-	 * Default destructor.
-	 */
-	virtual ~Connection() = default;
-
-	/**
-	 * Wait for the next requested response.
-	 *
-	 * @param name the response name
-	 * @param timeout the optional timeout
-	 * @return the object
-	 * @throw net::Error on errors or on timeout
-	 */
-	json::Value next(const std::string &name, int timeout = 30000);
-
-	/**
-	 * Just wait if the operation succeeded.
-	 *
-	 * @param name the response name
-	 * @param timeout the timeout
-	 */
-	void verify(const std::string &name, int timeout = 30000);
-
-	/**
-	 * Check if the socket is still connected.
-	 *
-	 * @return true if connected
-	 */
-	virtual bool isConnected() const noexcept = 0;
-
-	/**
-	 * Try to connect to the host.
-	 *
-	 * @param timeout the maximum time in milliseconds
-	 * @throw net::Error on errors or timeout
-	 */
-	virtual void connect(int timeout = 30000) = 0;
-
-	/**
-	 * Try to send the message in 30 seconds. The message must not end with \r\n\r\n, it is added automatically.
-	 *
-	 * @pre msg must not be empty
-	 * @param msg the message to send
-	 * @param timeout the maximum time in milliseconds
-	 * @throw net::Error on errors
-	 */
-	virtual void send(std::string msg, int timeout = 30000) = 0;
-
-	/**
-	 * Get the next event from irccd.
-	 *
-	 * This functions throws if the connection is lost.
-	 *
-	 * @param timeout the maximum time in milliseconds
-	 * @return the next event
-	 * @throw net::Error on errors or disconnection
-	 */
-	virtual json::Value next(int timeout = 30000) = 0;
-};
-
-/**
- * @class ConnectionBase
- * @brief Implementation for Ip or Local.
- */
-template <typename Address>
-class ConnectionBase : public Connection {
-private:
-	net::SocketTcp<Address> m_socket;
-	net::Listener<> m_listener;
-	Address m_address;
-
-	/* Input buffer */
-	std::string m_input;
-
-public:
-	/**
-	 * Construct the socket but do not connect immediately.
-	 *
-	 * @param address the address
-	 */
-	ConnectionBase(Address address)
-		: m_address(std::move(address))
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-		m_listener.set(m_socket.handle(), net::Condition::Readable);
-	}
-
-	/**
-	 * @copydoc Connection::isConnected
-	 */
-	bool isConnected() const noexcept override
-	{
-		return m_socket.state() == net::State::Connected;
-	}
-
-	/**
-	 * @copydoc Connection::connect
-	 */
-	void connect(int timeout) override;
-
-	/**
-	 * @copydoc Connection::send
-	 */
-	void send(std::string msg, int timeout) override;
-
-	/**
-	 * @copydoc Connection::next
-	 */
-	json::Value next(int timeout) override;
-};
-
-template <typename Address>
-void ConnectionBase<Address>::connect(int timeout)
-{
-	m_socket.connect(m_address);
-
-	if (m_socket.state() == net::State::Connecting) {
-		m_listener.set(m_socket.handle(), net::Condition::Writable);
-		m_listener.wait(timeout);
-		m_socket.connect();
-		m_listener.unset(m_socket.handle(), net::Condition::Writable);
-	}
-}
-
-template <typename Address>
-void ConnectionBase<Address>::send(std::string msg, int timeout)
-{
-	assert(!msg.empty());
-
-	/* Add termination */
-	msg += "\r\n\r\n";
-
-	m_listener.remove(m_socket.handle());
-	m_listener.set(m_socket.handle(), net::Condition::Writable);
-	m_timer.reset();
-
-	while (!msg.empty()) {
-		/* Do not wait the time that is already passed */
-		m_listener.wait(clamp(timeout));
-
-		/* Try to send at most as possible */
-		msg.erase(0, m_socket.send(msg));
-	}
-
-	/* Timeout? */
-	if (!msg.empty())
-		throw std::runtime_error("operation timed out while sending to irccd");
-}
-
-template <typename Address>
-json::Value ConnectionBase<Address>::next(int timeout)
-{
-	/* Maybe there is already something */
-	std::string buffer = util::nextNetwork(m_input);
-
-	m_listener.remove(m_socket.handle());
-	m_listener.set(m_socket.handle(), net::Condition::Readable);
-	m_timer.reset();
-
-	/* Read if there is nothing */
-	while (buffer.empty() && isConnected()) {
-		/* Wait and read */
-		m_listener.wait(clamp(timeout));
-		m_input += m_socket.recv(512);
-
-		/* Finally try */
-		buffer = util::nextNetwork(m_input);
-	}
-
-	if (!isConnected())
-		throw std::runtime_error("connection lost");
-
-	json::Value value(json::Buffer{buffer});
-
-	if (!value.isObject())
-		throw std::invalid_argument("invalid message received");
-
-	return value;
-}
-
-} // !irccd
-
-#endif // !_IRCCD_CONNECTION_H_
--- a/lib/irccd/private/directory.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * directory.cpp -- open and read directories
- *
- * 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 <sstream>
-#include <stdexcept>
-
-#include "directory.h"
-
-#if defined(_WIN32)
-#  include <Windows.h>
-#else
-#  include <cstring>
-#  include <cerrno>
-
-#  include <sys/types.h>
-#  include <dirent.h>
-#endif
-
-namespace irccd {
-
-#if defined(_WIN32)
-
-namespace {
-
-std::string systemError()
-{
-	LPSTR error = nullptr;
-	std::string errmsg = "Unknown error";
-
-	FormatMessageA(
-		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-		NULL,
-		GetLastError(),
-		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPSTR)&error, 0, NULL);
-
-	if (error) {
-		errmsg = std::string(error);
-		LocalFree(error);
-	}
-
-	return errmsg;
-}
-
-} // !namespace
-
-void Directory::systemLoad(const std::string &path, int flags)
-{
-	std::ostringstream oss;
-	HANDLE handle;
-	WIN32_FIND_DATA fdata;
-
-	oss << path << "\\*";
-	handle = FindFirstFile(oss.str().c_str(), &fdata);
-
-	if (handle == nullptr)
-		throw std::runtime_error(systemError());
-
-	do {
-		DirectoryEntry entry;
-
-		entry.name = fdata.cFileName;
-		if (entry.name == "." && !(flags & Directory::Dot))
-			continue;
-		if (entry.name == ".." && !(flags & Directory::DotDot))
-			continue;
-
-		switch (fdata.dwFileAttributes) {
-		case FILE_ATTRIBUTE_DIRECTORY:
-			entry.type = DirectoryEntry::Dir;
-			break;
-		case FILE_ATTRIBUTE_NORMAL:
-			entry.type = DirectoryEntry::File;
-			break;
-		case FILE_ATTRIBUTE_REPARSE_POINT:
-			entry.type = DirectoryEntry::Link;
-			break;
-		default:
-			break;
-		}
-
-		m_list.push_back(entry);
-	} while (FindNextFile(handle, &fdata) != 0);
-
-	FindClose(handle);
-}
-
-#else
-
-void Directory::systemLoad(const std::string &path, int flags)
-{
-	DIR *dp;
-	struct dirent *ent;
-
-	if ((dp = opendir(path.c_str())) == nullptr)
-		throw std::runtime_error(std::strerror(errno));
-
-	while ((ent = readdir(dp)) != nullptr) {
-		DirectoryEntry entry;
-
-		entry.name = ent->d_name;
-		if (entry.name == "." && !(flags & Directory::Dot))
-			continue;
-		if (entry.name == ".." && !(flags & Directory::DotDot))
-			continue;
-
-		switch (ent->d_type) {
-		case DT_DIR:
-			entry.type = DirectoryEntry::Dir;
-			break;
-		case DT_REG:
-			entry.type = DirectoryEntry::File;
-			break;
-		case DT_LNK:
-			entry.type = DirectoryEntry::Link;
-			break;
-		default:
-			break;
-		}
-
-		m_list.push_back(entry);
-	}
-
-	closedir(dp);
-}
-
-#endif
-
-bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2)
-{
-	return e1.name == e2.name && e1.type == e2.type;
-}
-
-Directory::Directory()
-{
-}
-
-Directory::Directory(const std::string &path, int flags)
-{
-	systemLoad(path, flags);
-}
-
-Directory::List::iterator Directory::begin()
-{
-	return m_list.begin();
-}
-
-Directory::List::const_iterator Directory::cbegin() const
-{
-	return m_list.cbegin();
-}
-
-Directory::List::iterator Directory::end()
-{
-	return m_list.end();
-}
-
-Directory::List::const_iterator Directory::cend() const
-{
-	return m_list.cend();
-}
-
-int Directory::count() const
-{
-	return m_list.size();
-}
-
-bool operator==(const Directory &d1, const Directory &d2)
-{
-	return d1.m_list == d2.m_list;
-}
-
-} // !irccd
--- a/lib/irccd/private/directory.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * directory.h -- open and read directories
- *
- * 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 _DIRECTORY_H_
-#define _DIRECTORY_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * @class Entry
- * @brief entry in the directory list
- */
-class DirectoryEntry {
-public:
-	/**
-	 * @enum Type
-	 * @brief Describe the type of an entry
-	 */
-	enum Type {
-		Unknown		= 0,
-		File,
-		Dir,
-		Link
-	};
-
-	std::string	name;		//! name of entry (base name)
-	Type		type{Unknown};	//! type of file
-
-	friend bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2);
-};
-
-/**
- * @class Directory
- * @brief class to manipulate directories
- *
- * This class allow the user to iterate directories in a for range based
- * loop using iterators.
- */
-class Directory {
-public:
-	/**
-	 * @enum Flags
-	 * @brief optional flags to read directories
-	 */
-	enum Flags {
-		Dot	= (1 << 0),	//!< If set, lists "." too
-		DotDot	= (1 << 1)	//!< If set, lists ".." too
-	};
-
-	using List = std::vector<DirectoryEntry>;
-
-	// C++ Container compatibility
-	using value_type	= List::value_type;
-	using iterator		= List::iterator;
-	using const_iterator	= List::const_iterator;
-
-private:
-	List m_list;
-
-	void systemLoad(const std::string &path, int flags);
-
-public:
-	/**
-	 * Default constructor, does nothing.
-	 */
-	Directory();
-
-	/**
-	 * Open a directory and read all its content.
-	 * @param path the path
-	 * @param flags the optional flags
-	 */
-	Directory(const std::string &path, int flags = 0);
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~Directory() = default;
-
-	/**
-	 * Return an iterator the beginning.
-	 *
-	 * @return the iterator
-	 */
-	List::iterator begin();
-
-	/**
-	 * Return a const iterator the beginning.
-	 *
-	 * @return the iterator
-	 */
-	List::const_iterator cbegin() const;
-
-	/**
-	 * Return an iterator to past the end.
-	 *
-	 * @return the iterator
-	 */
-	List::iterator end();
-
-	/**
-	 * Return a const iterator to past the end.
-	 *
-	 * @return the iterator
-	 */
-	List::const_iterator cend() const;
-
-	/**
-	 * Get the number of entries in the directory.
-	 *
-	 * @return the number
-	 */
-	int count() const;
-
-	friend bool operator==(const Directory &d1, const Directory &d2);
-};
-
-} // !irccd
-
-#endif // !_DIRECTORY_H_
--- a/lib/irccd/private/elapsed-timer.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * elapsed-timer.cpp -- measure elapsed time
- *
- * 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 "elapsed-timer.h"
-
-using std::chrono::duration_cast;
-using std::chrono::high_resolution_clock;
-using std::chrono::milliseconds;
-
-namespace irccd {
-
-ElapsedTimer::ElapsedTimer() noexcept
-{
-	m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::pause() noexcept
-{
-	/*
-	 * When we put the timer on pause, do not forget to set the already
-	 * elapsed time.
-	 */
-	(void)elapsed();
-	m_paused = true;
-}
-
-void ElapsedTimer::restart() noexcept
-{
-	m_paused = false;
-	m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::reset() noexcept
-{
-	m_elapsed = 0;
-	m_last = high_resolution_clock::now();
-}
-
-unsigned ElapsedTimer::elapsed() noexcept
-{
-	if (!m_paused) {
-		m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count();
-		m_last = high_resolution_clock::now();
-	}
-
-	return m_elapsed;
-}
-
-} // !irccd
--- a/lib/irccd/private/elapsed-timer.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * elapsed-timer.h -- measure elapsed time
- *
- * 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_ELAPSED_TIMER_H_
-#define _IRCCD_ELAPSED_TIMER_H_
-
-/**
- * @file ElapsedTimer.h
- * @brief Measure elapsed time
- */
-
-#include <chrono>
-
-namespace irccd {
-
-/**
- * @class ElapsedTimer
- * @brief Measure elapsed time
- *
- * This class provides an abstraction to measure elapsed time since the
- * construction of the object.
- *
- * It uses std::chrono::high_resolution_clock for more precision and uses
- * milliseconds only.
- */
-class ElapsedTimer {
-public:
-	using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
-
-private:
-	TimePoint m_last;
-	bool m_paused{false};
-	unsigned m_elapsed{0};
-
-public:
-	/**
-	 * Construct the elapsed timer, start counting.
-	 */
-	ElapsedTimer() noexcept;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~ElapsedTimer() = default;
-
-	/**
-	 * Put the timer on pause, the already elapsed time is stored.
-	 */
-	void pause() noexcept;
-
-	/**
-	 * Restart the timer, does not reset it.
-	 */
-	void restart() noexcept;
-
-	/**
-	 * Reset the timer to 0.
-	 */
-	void reset() noexcept;
-
-	/**
-	 * Get the number of elapsed milliseconds.
-	 *
-	 * @return the milliseconds
-	 */
-	unsigned elapsed() noexcept;
-};
-
-} // !irccd
-
-#endif // !_IRCCD_ELAPSED_TIMER_H_
--- a/lib/irccd/private/filesystem.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-/*
- * filesystem.cpp -- some file system operation
- *
- * 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 <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <stdexcept>
-#include <sstream>
-
-#include <irccd-config.h>
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-#  include <direct.h>
-#  include <shlwapi.h>
-#else
-#  include <sys/stat.h>
-#  include <climits>
-#  include <unistd.h>
-#  include <libgen.h>
-#endif
-
-#include "filesystem.h"
-
-namespace irccd {
-
-namespace fs {
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-const char Separator('\\');
-#else
-const char Separator('/');
-#endif
-
-std::string baseName(std::string path)
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	std::size_t pos;
-
-	pos = path.find_last_of('\\');
-	if (pos == std::string::npos)
-		pos = path.find_last_of('/');
-	if (pos == std::string::npos)
-		return path;
-
-	return path.substr(pos + 1);
-#else
-	return basename(&path[0]);
-#endif
-}
-
-std::string dirName(std::string path)
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	std::size_t pos;
-
-	pos = path.find_last_of('\\');
-	if (pos == std::string::npos)
-		pos = path.find_last_of('/');
-	if (pos == std::string::npos)
-		return path;
-
-	return path.substr(0, pos);
-#else
-	return dirname(&path[0]);
-#endif
-}
-
-bool isAbsolute(const std::string &path) noexcept
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	return !isRelative(path);
-#else
-	return path.size() > 0 && path[0] == '/';
-#endif
-}
-
-bool isRelative(const std::string &path) noexcept
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	return PathIsRelativeA(path.c_str());
-#else
-	return !isAbsolute(path);
-#endif
-}
-
-bool exists(const std::string &path)
-{
-#if defined(HAVE_ACCESS)
-	return access(path.c_str(), F_OK) == 0;
-#elif defined(HAVE_STAT)
-	struct stat st;
-
-	return (stat(path.c_str(), &st) == 0);
-#else
-	// worse fallback
-	std::FILE *file = std::fopen(path.c_str(), "r");
-	bool result;
-
-	if (file != nullptr) {
-		result = true;
-		std::fclose(file);
-	} else {
-		result = false;
-	}
-
-	return result;
-#endif
-}
-
-void mkdir(const std::string &dir, int mode)
-{
-	std::ostringstream oss;
-
-	oss << "mkdir: ";
-
-	for (std::size_t i = 0; i < dir.length(); ++i) {
-		if (dir[i] != '/' && dir[i] != '\\')
-			continue;
-
-		std::string part = dir.substr(0, i);
-		if (part.length() <= 0 || exists(part))
-			continue;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-		if (::_mkdir(part.c_str()) < 0) {
-#else
-		if (::mkdir(part.c_str(), mode) < 0) {
-#endif
-			oss << part << ": " << std::strerror(errno);
-			throw std::runtime_error(oss.str());
-		}
-	}
-
-	// Last part
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	if (::_mkdir(dir.c_str()) < 0) {
-#else
-	if (::mkdir(dir.c_str(), mode) < 0) {
-#endif
-		oss << dir << ": " << std::strerror(errno);
-		throw std::runtime_error(oss.str());
-	}
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	// Windows's mkdir does not use mode.
-	(void)mode;
-#endif
-}
-
-std::string cwd()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	char path[MAX_PATH];
-
-	if (!GetCurrentDirectoryA(sizeof (path), path))
-		throw std::runtime_error("failed to get current working directory");
-
-	return path;
-#else
-	char path[PATH_MAX];
-
-	if (getcwd(path, sizeof (path)) == nullptr)
-		throw std::runtime_error(std::strerror(errno));
-
-	return path;
-#endif
-}
-
-} // !fs
-
-} // !irccd
--- a/lib/irccd/private/filesystem.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * filesystem.h -- some file system operation
- *
- * 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_FILESYSTEM_H_
-#define _IRCCD_FILESYSTEM_H_
-
-#include <string>
-
-namespace irccd {
-
-namespace fs {
-
-extern const char Separator;
-
-std::string baseName(std::string path);
-std::string dirName(std::string path);
-bool isAbsolute(const std::string &path) noexcept;
-bool isRelative(const std::string &path) noexcept;
-bool exists(const std::string &path);
-void mkdir(const std::string &dir, int mode = 0700);
-std::string cwd();
-
-} // !fs
-
-} // !irccd
-
-#endif // !_IRCCD_FILESYSTEM_H_
--- a/lib/irccd/private/ini.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-/*
- * ini.cpp -- .ini file parsing
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <cassert>
-#include <cctype>
-#include <cstring>
-#include <iostream>
-#include <iterator>
-#include <fstream>
-#include <sstream>
-#include <stdexcept>
-
-#if defined(_WIN32)
-#  include <Shlwapi.h>	// for PathIsRelative
-#endif
-
-#include "ini.h"
-
-namespace {
-
-using namespace irccd;
-using namespace irccd::ini;
-
-using StreamIterator = std::istreambuf_iterator<char>;
-using TokenIterator = std::vector<Token>::const_iterator;
-
-inline bool isAbsolute(const std::string &path) noexcept
-{
-#if defined(_WIN32)
-	return !PathIsRelative(path.c_str());
-#else
-	return path.size() > 0 && path[0] == '/';
-#endif
-}
-
-inline bool isQuote(char c) noexcept
-{
-	return c == '\'' || c == '"';
-}
-
-inline bool isSpace(char c) noexcept
-{
-	/* Custom version because std::isspace includes \n as space */
-	return c == ' ' || c == '\t';
-}
-
-inline bool isList(char c) noexcept
-{
-	return c == '(' || c == ')' || c == ',';
-}
-
-inline bool isReserved(char c) noexcept
-{
-	return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '=';
-}
-
-void analyzeLine(int &line, int &column, StreamIterator &it) noexcept
-{
-	assert(*it == '\n');
-
-	++ line;
-	++ it;
-	column = 0;
-}
-
-void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-	assert(*it == '#');
-
-	while (it != end && *it != '\n') {
-		++ column;
-		++ it;
-	}
-}
-
-void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-	assert(isSpace(*it));
-
-	while (it != end && isSpace(*it)) {
-		++ column;
-		++ it;
-	}
-}
-
-void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept
-{
-	assert(isList(*it));
-
-	switch (*it++) {
-	case '(':
-		list.emplace_back(Token::ListBegin, line, column++);
-		break;
-	case ')':
-		list.emplace_back(Token::ListEnd, line, column++);
-		break;
-	case ',':
-		list.emplace_back(Token::Comma, line, column++);
-		break;
-	default:
-		break;
-	}
-}
-
-void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(*it == '[');
-
-	std::string value;
-	int save = column;
-
-	/* Read section name */
-	++ it;
-	while (it != end && *it != ']') {
-		if (*it == '\n')
-			throw Error(line, column, "section not terminated, missing ']'");
-		if (isReserved(*it))
-			throw Error(line, column, "section name expected after '[', got '" + std::string(1, *it) + "'");
-		++ column;
-		value += *it++;
-	}
-
-	if (it == end)
-		throw Error(line, column, "section name expected after '[', got <EOF>");
-
-	/* Remove ']' */
-	++ it;
-
-	list.emplace_back(Token::Section, line, save, std::move(value));
-}
-
-void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it)
-{
-	assert(*it == '=');
-
-	list.push_back({ Token::Assign, line, column++ });
-	++ it;
-}
-
-void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	std::string value;
-	int save = column;
-	char quote = *it++;
-
-	while (it != end && *it != quote) {
-		// TODO: escape sequence
-		++ column;
-		value += *it++;
-	}
-
-	if (it == end)
-		throw Error(line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>");
-
-	/* Remove quote */
-	++ it;
-
-	list.push_back({ Token::QuotedWord, line, save, std::move(value) });
-}
-
-void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(!isReserved(*it));
-
-	std::string value;
-	int save = column;
-
-	while (it != end && !std::isspace(*it) && !isReserved(*it)) {
-		++ column;
-		value += *it++;
-	}
-
-	list.push_back({ Token::Word, line, save, std::move(value) });
-}
-
-void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(*it == '@');
-
-	std::string include;
-	int save = column;
-
-	/* Read include */
-	++ it;
-	while (it != end && !isSpace(*it)) {
-		++ column;
-		include += *it++;
-	}
-
-	if (include != "include")
-		throw Error(line, column, "expected include after '@' token");
-
-	list.push_back({ Token::Include, line, save });
-}
-
-Tokens analyze(StreamIterator &it, StreamIterator end)
-{
-	Tokens list;
-	int line = 1;
-	int column = 0;
-
-	while (it != end) {
-		if (*it == '\n')
-			analyzeLine(line, column, it);
-		else if (*it == '#')
-			analyzeComment(column, it, end);
-		else if (*it == '[')
-			analyzeSection(list, line, column, it, end);
-		else if (*it == '=')
-			analyzeAssign(list, line, column, it);
-		else if (isSpace(*it))
-			analyzeSpaces(column, it, end);
-		else if (*it == '@')
-			analyzeInclude(list, line, column, it, end);
-		else if (isQuote(*it))
-			analyzeQuotedWord(list, line, column, it, end);
-		else if (isList(*it))
-			analyzeList(list, line, column, it);
-		else
-			analyzeWord(list, line, column, it, end);
-	}
-
-	return list;
-}
-
-void parseOptionValueSimple(Option &option, TokenIterator &it)
-{
-	assert(it->type() == Token::Word || it->type() == Token::QuotedWord);
-
-	option.push_back((it++)->value());
-}
-
-void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end)
-{
-	assert(it->type() == Token::ListBegin);
-
-	TokenIterator save = it++;
-
-	while (it != end && it->type() != Token::ListEnd) {
-		switch (it->type()) {
-		case Token::Comma:
-			/* Previous must be a word */
-			if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord)
-				throw Error(it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'");
-
-			++ it;
-			break;
-		case Token::Word:
-		case Token::QuotedWord:
-			option.push_back((it++)->value());
-			break;
-		default:
-			throw Error(it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct");
-			break;
-		}
-	}
-
-	if (it == end)
-		throw Error(save->line(), save->column(), "unterminated list construct");
-
-	/* Remove ) */
-	++ it;
-}
-
-void parseOption(Section &sc, TokenIterator &it, TokenIterator end)
-{
-	Option option(it->value());
-
-	TokenIterator save = it;
-
-	/* No '=' or something else? */
-	if (++it == end)
-		throw Error(save->line(), save->column(), "expected '=' assignment, got <EOF>");
-	if (it->type() != Token::Assign)
-		throw Error(it->line(), it->column(), "expected '=' assignment, got " + it->value());
-
-	/* Empty options are allowed so just test for words */
-	if (++it != end) {
-		if (it->type() == Token::Word || it->type() == Token::QuotedWord)
-			parseOptionValueSimple(option, it);
-		else if (it->type() == Token::ListBegin)
-			parseOptionValueList(option, it, end);
-	}
-
-	sc.push_back(std::move(option));
-}
-
-void parseInclude(Document &doc, TokenIterator &it, TokenIterator end)
-{
-	TokenIterator save = it;
-
-	if (++it == end)
-		throw Error(save->line(), save->column(), "expected file name after '@include' statement, got <EOF>");
-	if (it->type() != Token::Word && it->type() != Token::QuotedWord)
-		throw Error(it->line(), it->column(), "expected file name after '@include' statement, got " + it->value());
-	if (doc.path().empty())
-		throw Error(it->line(), it->column(), "'@include' statement invalid with buffer documents");
-
-	std::string value = (it++)->value();
-	std::string file;
-
-	if (!isAbsolute(value)) {
-#if defined(_WIN32)
-		file = doc.path() + "\\" + value;
-#else
-		file = doc.path() + "/" + value;
-#endif
-	} else {
-		file = value;
-	}
-
-	Document child(File{file});
-
-	for (const auto &sc : child)
-		doc.push_back(sc);
-}
-
-void parseSection(Document &doc, TokenIterator &it, TokenIterator end)
-{
-	Section sc(it->value());
-
-	/* Skip [section] */
-	++ it;
-
-	/* Read until next section */
-	while (it != end && it->type() != Token::Section) {
-		if (it->type() != Token::Word)
-			throw Error(it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition");
-
-		parseOption(sc, it, end);
-	}
-
-	doc.push_back(std::move(sc));
-}
-
-void parse(Document &doc, const Tokens &tokens)
-{
-	TokenIterator it = tokens.cbegin();
-	TokenIterator end = tokens.cend();
-
-	while (it != end) {
-		/* Just ignore this */
-		switch (it->type()) {
-		case Token::Include:
-			parseInclude(doc, it, end);
-			break;
-		case Token::Section:
-			parseSection(doc, it, end);
-			break;
-		default:
-			throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document");
-		}
-	}
-}
-
-} // !namespace
-
-namespace irccd {
-
-namespace ini {
-
-Tokens Document::analyze(const File &file)
-{
-	std::fstream stream(file.path);
-
-	if (!stream)
-		throw std::runtime_error(std::strerror(errno));
-
-	std::istreambuf_iterator<char> it(stream);
-	std::istreambuf_iterator<char> end;
-
-	return ::analyze(it, end);
-}
-
-Tokens Document::analyze(const Buffer &buffer)
-{
-	std::istringstream stream(buffer.text);
-	std::istreambuf_iterator<char> it(stream);
-	std::istreambuf_iterator<char> end;
-
-	return ::analyze(it, end);
-}
-
-Document::Document(const File &file)
-	: m_path(file.path)
-{
-	/* Update path */
-	auto pos = m_path.find_last_of("/\\");
-
-	if (pos != std::string::npos)
-		m_path.erase(pos);
-	else
-		m_path = ".";
-
-	parse(*this, analyze(file));
-}
-
-Document::Document(const Buffer &buffer)
-{
-	parse(*this, analyze(buffer));
-}
-
-void Document::dump(const Tokens &tokens)
-{
-	for (const Token &token: tokens)
-		// TODO: add better description
-		std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl;
-}
-
-} // !ini
-
-} // !irccd
--- a/lib/irccd/private/ini.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,538 +0,0 @@
-/*
- * ini.h -- .ini file parsing
- *
- * 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 _INI_H_
-#define _INI_H_
-
-/**
- * @file Ini.h
- * @brief Configuration file parser.
- */
-
-#include <algorithm>
-#include <exception>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * Namespace for ini related classes.
- */
-namespace ini {
-
-class Document;
-
-/**
- * @class Error
- * @brief Error in a file
- */
-class Error : public std::exception {
-private:
-	int m_line;		//!< line number
-	int m_column;		//!< line column
-	std::string m_message;	//!< error message
-
-public:
-	/**
-	 * Constructor.
-	 *
-	 * @param l the line
-	 * @param c the column
-	 * @param m the message
-	 */
-	inline Error(int l, int c, std::string m) noexcept
-		: m_line(l)
-		, m_column(c)
-		, m_message(std::move(m))
-	{
-	}
-
-	/**
-	 * Get the line number.
-	 *
-	 * @return the line
-	 */
-	inline int line() const noexcept
-	{
-		return m_line;
-	}
-
-	/**
-	 * Get the column number.
-	 *
-	 * @return the column
-	 */
-	inline int column() const noexcept
-	{
-		return m_column;
-	}
-
-	/**
-	 * Return the raw error message (no line and column shown).
-	 *
-	 * @return the error message
-	 */
-	const char *what() const noexcept override
-	{
-		return m_message.c_str();
-	}
-};
-
-/**
- * @class Token
- * @brief Describe a token read in the .ini source
- *
- * This class can be used when you want to parse a .ini file yourself.
- *
- * @see Document::analyze
- */
-class Token {
-public:
-	/**
-	 * @brief Token type
-	 */
-	enum Type {
-		Include,	//!< include statement
-		Section,	//!< [section]
-		Word,		//!< word without quotes
-		QuotedWord,	//!< word with quotes
-		Assign,		//!< = assignment
-		ListBegin,	//!< begin of list (
-		ListEnd,	//!< end of list )
-		Comma		//!< list separation
-	};
-
-private:
-	Type m_type;
-	int m_line;
-	int m_column;
-	std::string m_value;
-
-public:
-	/**
-	 * Construct a token.
-	 *
-	 * @param type the type
-	 * @param line the line
-	 * @param column the column
-	 * @param value the value
-	 */
-	Token(Type type, int line, int column, std::string value = "") noexcept
-		: m_type(type)
-		, m_line(line)
-		, m_column(column)
-	{
-		switch (type) {
-		case Include:
-			m_value = "@include";
-			break;
-		case Section:
-		case Word:
-		case QuotedWord:
-			m_value = value;
-			break;
-		case Assign:
-			m_value = "=";
-			break;
-		case ListBegin:
-			m_value = "(";
-			break;
-		case ListEnd:
-			m_value = ")";
-			break;
-		case Comma:
-			m_value = ",";
-			break;
-		default:
-			break;
-		}
-	}
-
-	/**
-	 * Get the type.
-	 *
-	 * @return the type
-	 */
-	inline Type type() const noexcept
-	{
-		return m_type;
-	}
-
-	/**
-	 * Get the line.
-	 *
-	 * @return the line
-	 */
-	inline int line() const noexcept
-	{
-		return m_line;
-	}
-
-	/**
-	 * Get the column.
-	 *
-	 * @return the column
-	 */
-	inline int column() const noexcept
-	{
-		return m_column;
-	}
-
-	/**
-	 * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the
-	 * characters parsed.
-	 *
-	 * @return the value
-	 */
-	inline const std::string &value() const noexcept
-	{
-		return m_value;
-	}
-};
-
-/**
- * List of tokens in order they are analyzed.
- */
-using Tokens = std::vector<Token>;
-
-/**
- * @class Option
- * @brief Option definition.
- */
-class Option : public std::vector<std::string> {
-private:
-	std::string m_key;
-
-public:
-	/**
-	 * Construct an empty option.
-	 *
-	 * @param key the key
-	 * @param value the value
-	 */
-	inline Option(std::string key) noexcept
-		: std::vector<std::string>()
-		, m_key(std::move(key))
-	{
-	}
-
-	/**
-	 * Construct a single option.
-	 *
-	 * @param key the key
-	 * @param value the value
-	 */
-	inline Option(std::string key, std::string value) noexcept
-		: m_key(std::move(key))
-	{
-		push_back(std::move(value));
-	}
-
-	/**
-	 * Construct a list option.
-	 *
-	 * @param key the key
-	 * @param values the values
-	 */
-	inline Option(std::string key, std::vector<std::string> values) noexcept
-		: std::vector<std::string>(std::move(values))
-		, m_key(std::move(key))
-	{
-	}
-
-	/**
-	 * Get the option key.
-	 *
-	 * @return the key
-	 */
-	inline const std::string &key() const noexcept
-	{
-		return m_key;
-	}
-
-	/**
-	 * Get the option value.
-	 *
-	 * @return the value
-	 */
-	inline const std::string &value() const noexcept
-	{
-		static std::string dummy;
-
-		return empty() ? dummy : (*this)[0];
-	}
-};
-
-/**
- * @class Section
- * @brief Section that contains one or more options.
- */
-class Section : public std::vector<Option> {
-private:
-	std::string m_key;
-
-public:
-	/**
-	 * Construct a section with its name.
-	 *
-	 * @param key the key
-	 */
-	inline Section(std::string key) noexcept
-		: m_key(std::move(key))
-	{
-	}
-
-	/**
-	 * Get the section key.
-	 *
-	 * @return the key
-	 */
-	inline const std::string &key() const noexcept
-	{
-		return m_key;
-	}
-
-	/**
-	 * Check if the section contains a specific option.
-	 *
-	 * @param key the option key
-	 * @return true if the option exists
-	 */
-	inline bool contains(const std::string &key) const noexcept
-	{
-		return find(key) != end();
-	}
-
-	/**
-	 * Access an option at the specified key.
-	 *
-	 * @param key the key
-	 * @return the option
-	 * @throw std::out_of_range if the key does not exist
-	 */
-	inline Option &operator[](const std::string &key)
-	{
-		return *find(key);
-	}
-
-	/**
-	 * Access an option at the specified key.
-	 *
-	 * @param key the key
-	 * @return the option
-	 * @throw std::out_of_range if the key does not exist
-	 */
-	inline const Option &operator[](const std::string &key) const
-	{
-		return *find(key);
-	}
-
-	/**
-	 * Find an option by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline iterator find(const std::string &key) noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Find an option by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline const_iterator find(const std::string &key) const noexcept
-	{
-		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Inherited operators.
-	 */
-	using std::vector<Option>::operator[];
-};
-
-/**
- * @class File
- * @brief Source for reading .ini files.
- */
-class File {
-public:
-	/**
-	 * Path to the file.
-	 */
-	std::string path;
-};
-
-/**
- * @class Buffer
- * @brief Source for reading ini from text.
- * @note the include statement is not supported with buffers.
- */
-class Buffer {
-public:
-	/**
-	 * The ini content.
-	 */
-	std::string text;
-};
-
-/**
- * @class Document
- * @brief Ini config file loader
- */
-class Document : public std::vector<Section> {
-private:
-	std::string m_path;
-
-public:
-	/**
-	 * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid,
-	 * it just means that there are no syntax error.
-	 *
-	 * For example, this class does not allow adding options under no sections and this function will not
-	 * detect that issue.
-	 *
-	 * @param file the file to read
-	 * @return the list of tokens
-	 * @throws Error on errors
-	 */
-	static Tokens analyze(const File &file);
-
-	/**
-	 * Overloaded function for buffers.
-	 *
-	 * @param buffer the buffer to read
-	 * @return the list of tokens
-	 * @throws Error on errors
-	 */
-	static Tokens analyze(const Buffer &buffer);
-
-	/**
-	 * Show all tokens and their description.
-	 *
-	 * @param tokens the tokens
-	 */
-	static void dump(const Tokens &tokens);
-
-	/**
-	 * Construct a document from a file.
-	 *
-	 * @param file the file to read
-	 * @throws Error on errors
-	 */
-	Document(const File &file);
-
-	/**
-	 * Overloaded constructor for buffers.
-	 *
-	 * @param buffer the buffer to read
-	 * @throws Error on errors
-	 */
-	Document(const Buffer &buffer);
-
-	/**
-	 * Get the current document path, only useful when constructed from File source.
-	 *
-	 * @return the path
-	 */
-	inline const std::string &path() const noexcept
-	{
-		return m_path;
-	}
-
-	/**
-	 * Check if a document has a specific section.
-	 *
-	 * @param key the key
-	 */
-	inline bool contains(const std::string &key) const noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end();
-	}
-
-	/**
-	 * Access a section at the specified key.
-	 *
-	 * @param key the key
-	 * @return the section
-	 * @throw std::out_of_range if the key does not exist
-	 */
-	inline Section &operator[](const std::string &key)
-	{
-		return *find(key);
-	}
-
-	/**
-	 * Access a section at the specified key.
-	 *
-	 * @param key the key
-	 * @return the section
-	 * @throw std::out_of_range if the key does not exist
-	 */
-	inline const Section &operator[](const std::string &key) const
-	{
-		return *find(key);
-	}
-
-	/**
-	 * Find a section by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline iterator find(const std::string &key) noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Find a section by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline const_iterator find(const std::string &key) const noexcept
-	{
-		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Inherited operators.
-	 */
-	using std::vector<Section>::operator[];
-};
-
-} // !ini
-
-} // !irccd
-
-#endif // !_INI_H_
--- a/lib/irccd/private/signals.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-/*
- * signals.h -- synchronous observer mechanism
- *
- * 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_SIGNALS_H_
-#define _IRCCD_SIGNALS_H_
-
-#include <functional>
-#include <stack>
-#include <unordered_map>
-#include <vector>
-
-/**
- * @file Signals.h
- * @brief Similar Qt signal subsystem for irccd
- */
-
-namespace irccd {
-
-/**
- * @class SignalConnection
- * @brief Stores the reference to the callable
- *
- * This class can be stored to remove a registered function from a Signal, be
- * careful to not mix connections between different signals as they are just
- * referenced by ids.
- */
-class SignalConnection {
-private:
-	unsigned m_id;
-
-public:
-	/**
-	 * Create a signal connection.
-	 *
-	 * @param id the id
-	 */
-	inline SignalConnection(unsigned id) noexcept
-		: m_id(id)
-	{
-	}
-
-	/**
-	 * Get the reference object.
-	 *
-	 * @return the id
-	 */
-	inline unsigned id() const noexcept
-	{
-		return m_id;
-	}
-};
-
-/**
- * @class Signal
- * @brief Stores and call registered functions
- *
- * This class is intended to be use as a public field in the desired object.
- *
- * The user just have to call one of connect(), disconnect() or the call
- * operator to use this class.
- *
- * It stores the callable as std::function so type-erasure is complete.
- *
- * The user is responsible of taking care that the object is still alive
- * in case that the function takes a reference to the object.
- */
-template <typename... Args>
-class Signal {
-private:
-	using Function = std::function<void (Args...)>;
-	using FunctionMap = std::unordered_map<unsigned, Function>;
-	using Stack = std::stack<unsigned>;
-
-	FunctionMap m_functions;
-	Stack m_stack;
-	unsigned m_max{0};
-
-public:
-	/**
-	 * Register a new function to the signal.
-	 *
-	 * @param function the function
-	 * @return the connection in case you want to remove it
-	 */
-	inline SignalConnection connect(Function function) noexcept
-	{
-		unsigned id;
-
-		if (!m_stack.empty()) {
-			id = m_stack.top();
-			m_stack.pop();
-		} else {
-			id = m_max ++;
-		}
-
-		m_functions.emplace(id, std::move(function));
-
-		return SignalConnection{id};
-	}
-
-	/**
-	 * Disconnect a connection.
-	 *
-	 * @param connection the connection
-	 * @warning Be sure that the connection belongs to that signal
-	 */
-	inline void disconnect(const SignalConnection &connection) noexcept
-	{
-		auto value = m_functions.find(connection.id());
-
-		if (value != m_functions.end()) {
-			m_functions.erase(connection.id());
-			m_stack.push(connection.id());
-		}
-	}
-
-	/**
-	 * Remove all registered functions.
-	 */
-	inline void clear()
-	{
-		m_functions.clear();
-		m_max = 0;
-
-		while (!m_stack.empty())
-			m_stack.pop();
-	}
-
-	/**
-	 * Call every functions.
-	 *
-	 * @param args the arguments to pass to the signal
-	 */
-	void operator()(Args... args) const
-	{
-		/*
-		 * Make a copy of the ids before iterating because the callbacks may eventually remove or modify
-		 * the list.
-		 */
-		std::vector<unsigned> ids;
-
-		for (auto &pair : m_functions)
-			ids.push_back(pair.first);
-
-		/*
-		 * Now iterate while checking if the next id is still available, however if any new signals were
-		 * added while iterating, they will not be called immediately.
-		 */
-		for (unsigned i : ids) {
-			auto it = m_functions.find(i);
-
-			if (it != m_functions.end())
-				it->second(args...);
-		}
-	}
-};
-
-} // !irccd
-
-#endif // !_IRCCD_SIGNALS_H_
--- a/lib/irccd/private/sockets.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,713 +0,0 @@
-/*
- * sockets.cpp -- portable C++ socket wrappers
- *
- * 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.
- */
-
-#define TIMEOUT_MSG "operation timeout"
-
-#include <algorithm>
-#include <atomic>
-#include <cstring>
-#include <mutex>
-
-#include "sockets.h"
-
-namespace irccd {
-
-namespace net {
-
-/*
- * Portable constants
- * ------------------------------------------------------------------
- */
-
-/* {{{ Constants */
-
-#if defined(_WIN32)
-
-const Handle Invalid{INVALID_SOCKET};
-const int Failure{SOCKET_ERROR};
-
-#else
-
-const Handle Invalid{-1};
-const int Failure{-1};
-
-#endif
-
-/* }}} */
-
-/*
- * Portable functions
- * ------------------------------------------------------------------
- */
-
-/* {{{ Functions */
-
-#if defined(_WIN32)
-
-namespace {
-
-static std::mutex s_mutex;
-static std::atomic<bool> s_initialized{false};
-
-} // !namespace
-
-#endif // !_WIN32
-
-void init() noexcept
-{
-#if defined(_WIN32)
-	std::lock_guard<std::mutex> lock(s_mutex);
-
-	if (!s_initialized) {
-		s_initialized = true;
-
-		WSADATA wsa;
-		WSAStartup(MAKEWORD(2, 2), &wsa);
-
-		/*
-		 * If SOCKET_WSA_NO_INIT is not set then the user
-		 * must also call finish himself.
-		 */
-#if !defined(SOCKET_NO_AUTO_INIT)
-		atexit(finish);
-#endif
-	}
-#endif
-}
-
-void finish() noexcept
-{
-#if defined(_WIN32)
-	WSACleanup();
-#endif
-}
-
-std::string error(int errn)
-{
-#if defined(_WIN32)
-	LPSTR str = nullptr;
-	std::string errmsg = "Unknown error";
-
-	FormatMessageA(
-		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-		NULL,
-		errn,
-		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPSTR)&str, 0, NULL);
-
-
-	if (str) {
-		errmsg = std::string(str);
-		LocalFree(str);
-	}
-
-	return errmsg;
-#else
-	return strerror(errn);
-#endif
-}
-
-std::string error()
-{
-#if defined(_WIN32)
-	return error(WSAGetLastError());
-#else
-	return error(errno);
-#endif
-}
-
-/* }}} */
-
-/*
- * SSL stuff
- * ------------------------------------------------------------------
- */
-
-/* {{{ SSL initialization */
-
-#if !defined(SOCKET_NO_SSL)
-
-namespace ssl {
-
-namespace {
-
-std::mutex mutex;
-std::atomic<bool> initialized{false};
-
-} // !namespace
-
-void finish() noexcept
-{
-	ERR_free_strings();
-}
-
-void init() noexcept
-{
-	std::lock_guard<std::mutex> lock{mutex};
-
-	if (!initialized) {
-		initialized = true;
-
-		SSL_library_init();
-		SSL_load_error_strings();
-		OpenSSL_add_all_algorithms();
-
-#if !defined(SOCKET_NO_AUTO_SSL_INIT)
-		atexit(finish);
-#endif // SOCKET_NO_AUTO_SSL_INIT
-	}
-}
-
-} // !ssl
-
-#endif // SOCKET_NO_SSL
-
-/* }}} */
-
-/*
- * Error class
- * ------------------------------------------------------------------
- */
-
-/* {{{ Error */
-
-Error::Error(Code code, std::string function)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{error()}
-{
-}
-
-Error::Error(Code code, std::string function, int n)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{error(n)}
-{
-}
-
-Error::Error(Code code, std::string function, std::string error)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{std::move(error)}
-{
-}
-
-/* }}} */
-
-/*
- * Predefine addressed to be used
- * ------------------------------------------------------------------
- */
-
-/* {{{ Addresses */
-
-namespace address {
-
-/* Default domain */
-int Ip::m_default{AF_INET};
-
-Ip::Ip(Type domain) noexcept
-	: m_domain(static_cast<int>(domain))
-{
-	assert(m_domain == AF_INET6 || m_domain == AF_INET);
-
-	if (m_domain == AF_INET6) {
-		std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
-	} else {
-		std::memset(&m_sin, 0, sizeof (sockaddr_in));
-	}
-}
-
-Ip::Ip(const std::string &host, int port, Type domain)
-	: m_domain(static_cast<int>(domain))
-{
-	assert(m_domain == AF_INET6 || m_domain == AF_INET);
-
-	if (host == "*") {
-		if (m_domain == AF_INET6) {
-			std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
-
-			m_length = sizeof (sockaddr_in6);
-			m_sin6.sin6_addr = in6addr_any;
-			m_sin6.sin6_family = AF_INET6;
-			m_sin6.sin6_port = htons(port);
-		} else {
-			std::memset(&m_sin, 0, sizeof (sockaddr_in));
-
-			m_length = sizeof (sockaddr_in);
-			m_sin.sin_addr.s_addr = INADDR_ANY;
-			m_sin.sin_family = AF_INET;
-			m_sin.sin_port = htons(port);
-		}
-	} else {
-		addrinfo hints, *res;
-
-		std::memset(&hints, 0, sizeof (addrinfo));
-		hints.ai_family = domain;
-
-		auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
-		if (error != 0) {
-			throw Error{Error::System, "getaddrinfo", gai_strerror(error)};
-		}
-
-		if (m_domain == AF_INET6) {
-			std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen);
-		} else {
-			std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen);
-		}
-
-		m_length = res->ai_addrlen;
-		freeaddrinfo(res);
-	}
-}
-
-Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept
-	: m_length{length}
-	, m_domain{ss->ss_family}
-{
-	assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET);
-
-	if (ss->ss_family == AF_INET6) {
-		std::memcpy(&m_sin6, ss, length);
-	} else if (ss->ss_family == AF_INET) {
-		std::memcpy(&m_sin, ss, length);
-	}
-}
-
-#if !defined(_WIN32)
-
-Local::Local() noexcept
-{
-	std::memset(&m_sun, 0, sizeof (sockaddr_un));
-}
-
-Local::Local(std::string path, bool rm) noexcept
-	: m_path{std::move(path)}
-{
-	/* Silently remove the file even if it fails */
-	if (rm) {
-		::remove(m_path.c_str());
-	}
-
-	/* Copy the path */
-	std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path));
-	std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1);
-
-	/* Set the parameters */
-	m_sun.sun_family = AF_LOCAL;
-}
-
-Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept
-{
-	assert(ss->ss_family == AF_LOCAL);
-
-	if (ss->ss_family == AF_LOCAL) {
-		std::memcpy(&m_sun, ss, length);
-		m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path;
-	}
-}
-
-#endif // !_WIN32
-
-} // !address
-
-/* }}} */
-
-/*
- * Select
- * ------------------------------------------------------------------
- */
-
-/* {{{ Select */
-
-std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms)
-{
-	timeval maxwait, *towait;
-	fd_set readset;
-	fd_set writeset;
-
-	FD_ZERO(&readset);
-	FD_ZERO(&writeset);
-
-	Handle max = 0;
-
-	for (const auto &pair : table) {
-		if ((pair.second & Condition::Readable) == Condition::Readable) {
-			FD_SET(pair.first, &readset);
-		}
-		if ((pair.second & Condition::Writable) == Condition::Writable) {
-			FD_SET(pair.first, &writeset);
-		}
-
-		if (pair.first > max) {
-			max = pair.first;
-		}
-	}
-
-	maxwait.tv_sec = 0;
-	maxwait.tv_usec = ms * 1000;
-
-	// Set to nullptr for infinite timeout.
-	towait = (ms < 0) ? nullptr : &maxwait;
-
-	auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
-	if (error == Failure) {
-		throw Error{Error::System, "select"};
-	}
-	if (error == 0) {
-		throw Error{Error::Timeout, "select", TIMEOUT_MSG};
-	}
-
-	std::vector<ListenerStatus> sockets;
-
-	for (const auto &pair : table) {
-		if (FD_ISSET(pair.first, &readset)) {
-			sockets.push_back(ListenerStatus{pair.first, Condition::Readable});
-		}
-		if (FD_ISSET(pair.first, &writeset)) {
-			sockets.push_back(ListenerStatus{pair.first, Condition::Writable});
-		}
-	}
-
-	return sockets;
-}
-
-/* }}} */
-
-/*
- * Poll
- * ------------------------------------------------------------------
- */
-
-/* {{{ Poll */
-
-/*
- * Poll implementation
- * ------------------------------------------------------------------
- */
-
-#if defined(SOCKET_HAVE_POLL)
-
-#if defined(_WIN32)
-#  define poll WSAPoll
-#endif
-
-short Poll::toPoll(Condition condition) const noexcept
-{
-	short result(0);
-
-	if ((condition & Condition::Readable) == Condition::Readable) {
-		result |= POLLIN;
-	}
-	if ((condition & Condition::Writable) == Condition::Writable) {
-		result |= POLLOUT;
-	}
-
-	return result;
-}
-
-Condition Poll::toCondition(short &event) const noexcept
-{
-	Condition condition{Condition::None};
-
-	/*
-	 * Poll implementations mark the socket differently regarding
-	 * the disconnection of a socket.
-	 *
-	 * At least, even if POLLHUP or POLLIN is set, recv() always
-	 * return 0 so we mark the socket as readable.
-	 */
-	if ((event & POLLIN) || (event & POLLHUP)) {
-		condition |= Condition::Readable;
-	}
-	if (event & POLLOUT) {
-		condition |= Condition::Writable;
-	}
-
-	/* Reset event for safety */
-	event = 0;
-
-	return condition;
-}
-
-void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add)
-{
-	if (add) {
-		m_fds.push_back(pollfd{h, toPoll(condition), 0});
-	} else {
-		auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
-			return pfd.fd == h;
-		});
-
-		it->events |= toPoll(condition);
-	}
-}
-
-void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
-{
-	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
-		return pfd.fd == h;
-	});
-
-	if (remove) {
-		m_fds.erase(it);
-	} else {
-		it->events &= ~(toPoll(condition));
-	}
-}
-
-std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms)
-{
-	auto result = poll(m_fds.data(), m_fds.size(), ms);
-	if (result == 0) {
-		throw Error{Error::Timeout, "select", TIMEOUT_MSG};
-	}
-	if (result < 0) {
-		throw Error{Error::System, "poll"};
-	}
-
-	std::vector<ListenerStatus> sockets;
-	for (auto &fd : m_fds) {
-		if (fd.revents != 0) {
-			sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)});
-		}
-	}
-
-	return sockets;
-}
-
-#endif // !SOCKET_HAVE_POLL
-
-/* }}} */
-
-/*
- * Epoll implementation
- * ------------------------------------------------------------------
- */
-
-/* {{{ Epoll */
-
-#if defined(SOCKET_HAVE_EPOLL)
-
-uint32_t Epoll::toEpoll(Condition condition) const noexcept
-{
-	uint32_t events = 0;
-
-	if ((condition & Condition::Readable) == Condition::Readable) {
-		events |= EPOLLIN;
-	}
-	if ((condition & Condition::Writable) == Condition::Writable) {
-		events |= EPOLLOUT;
-	}
-
-	return events;
-}
-
-Condition Epoll::toCondition(uint32_t events) const noexcept
-{
-	Condition condition{Condition::None};
-
-	if ((events & EPOLLIN) || (events & EPOLLHUP)) {
-		condition |= Condition::Readable;
-	}
-	if (events & EPOLLOUT) {
-		condition |= Condition::Writable;
-	}
-
-	return condition;
-}
-
-void Epoll::update(Handle h, int op, int eflags)
-{
-	epoll_event ev;
-
-	std::memset(&ev, 0, sizeof (epoll_event));
-
-	ev.events = eflags;
-	ev.data.fd = h;
-
-	if (epoll_ctl(m_handle, op, h, &ev) < 0) {
-		throw Error{Error::System, "epoll_ctl"};
-	}
-}
-
-Epoll::Epoll()
-	: m_handle{epoll_create1(0)}
-{
-	if (m_handle < 0) {
-		throw Error{Error::System, "epoll_create"};
-	}
-}
-
-Epoll::~Epoll()
-{
-	close(m_handle);
-}
-
-/*
- * For set and unset, we need to apply the whole flags required, so if the socket
- * was set to Connection::Readable and user add Connection::Writable, we must
- * place both.
- */
-void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add)
-{
-	if (add) {
-		update(sc, EPOLL_CTL_ADD, toEpoll(condition));
-		m_events.resize(m_events.size() + 1);
-	} else {
-		update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition));
-	}
-}
-
-/*
- * Unset is a bit complicated case because Listener tells us which
- * flag to remove but to update epoll descriptor we need to pass
- * the effective flags that we want to be applied.
- *
- * So we put the same flags that are currently effective and remove the
- * requested one.
- */
-void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove)
-{
-	if (remove) {
-		update(sc, EPOLL_CTL_DEL, 0);
-		m_events.resize(m_events.size() - 1);
-	} else {
-		update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition)));
-	}
-}
-
-std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms)
-{
-	int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
-	std::vector<ListenerStatus> result;
-
-	if (ret == 0) {
-		throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG};
-	}
-	if (ret < 0) {
-		throw Error{Error::System, "epoll_wait"};
-	}
-
-	for (int i = 0; i < ret; ++i) {
-		result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
-	}
-
-	return result;
-}
-
-#endif // !SOCKET_HAVE_EPOLL
-
-/* }}} */
-
-/*
- * Kqueue implementation
- * ------------------------------------------------------------------
- */
-
-/* {{{ Kqueue */
-
-#if defined(SOCKET_HAVE_KQUEUE)
-
-Kqueue::Kqueue()
-	: m_handle(kqueue())
-{
-	if (m_handle < 0) {
-		throw Error{Error::System, "kqueue"};
-	}
-}
-
-Kqueue::~Kqueue()
-{
-	close(m_handle);
-}
-
-void Kqueue::update(Handle h, int filter, int kflags)
-{
-	struct kevent ev;
-
-	EV_SET(&ev, h, filter, kflags, 0, 0, nullptr);
-
-	if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) {
-		throw Error{Error::System, "kevent"};
-	}
-}
-
-void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add)
-{
-	if ((condition & Condition::Readable) == Condition::Readable) {
-		update(h, EVFILT_READ, EV_ADD | EV_ENABLE);
-	}
-	if ((condition & Condition::Writable) == Condition::Writable) {
-		update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE);
-	}
-
-	if (add) {
-		m_result.resize(m_result.size() + 1);
-	}
-}
-
-void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
-{
-	if ((condition & Condition::Readable) == Condition::Readable) {
-		update(h, EVFILT_READ, EV_DELETE);
-	}
-	if ((condition & Condition::Writable) == Condition::Writable) {
-		update(h, EVFILT_WRITE, EV_DELETE);
-	}
-
-	if (remove) {
-		m_result.resize(m_result.size() - 1);
-	}
-}
-
-std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms)
-{
-	std::vector<ListenerStatus> sockets;
-	timespec ts = { 0, 0 };
-	timespec *pts = (ms <= 0) ? nullptr : &ts;
-
-	ts.tv_sec = ms / 1000;
-	ts.tv_nsec = (ms % 1000) * 1000000;
-
-	int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts);
-
-	if (nevents == 0) {
-		throw Error{Error::Timeout, "kevent", TIMEOUT_MSG};
-	}
-	if (nevents < 0) {
-		throw Error{Error::System, "kevent"};
-	}
-
-	for (int i = 0; i < nevents; ++i) {
-		sockets.push_back(ListenerStatus{
-			static_cast<Handle>(m_result[i].ident),
-			m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable
-		});
-	}
-
-	return sockets;
-}
-
-#endif // !SOCKET_HAVE_KQUEUE
-
-/* }}} */
-
-} // !net
-
-} // !irccd
--- a/lib/irccd/private/sockets.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4078 +0,0 @@
-/*
- * sockets.h -- portable C++ socket wrappers
- *
- * 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 _SOCKETS_H_
-#define _SOCKETS_H_
-
-/**
- * @file Sockets.h
- * @brief Portable socket abstraction
- *
- * # Introduction
- *
- * This file is a portable networking library.
- *
- * ## Options
- *
- * The user may set the following variables before compiling these files:
- *
- * ### General options
- *
- * - **SOCKET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
- * automatically calls net::init function and net::finish functions.
- * - **SOCKET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL library.
- * - **SOCKET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class with Tls to automatically init
- * the OpenSSL library. You will need to call net::ssl::init and net::ssl::finish.
- *
- * ### Options for Listener class
- *
- * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll.
- *
- * We assume that `select(2)` is always available.
- *
- * Of course, you can set the variables yourself if you test it with your build system.
- *
- * - **SOCKET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows
- *   if _WIN32_WINNT is set to 0x0600 or greater.
- * - **SOCKET_HAVE_KQUEUE**: Defined on all BSD and Apple.
- * - **SOCKET_HAVE_EPOLL**: Defined on Linux only.
- * - **SOCKET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`).
- *
- * The preference priority is ordered from left to right.
- *
- * | System        | Backend                 | Class name   |
- * |---------------|-------------------------|--------------|
- * | Linux         | epoll(7)                | Epoll        |
- * | *BSD          | kqueue(2)               | Kqueue       |
- * | Windows       | poll(2), select(2)      | Poll, Select |
- * | Mac OS X      | kqueue(2)               | Kqueue       |
- */
-
-#if defined(_WIN32)
-#  if _WIN32_WINNT >= 0x0600 && !defined(SOCKET_HAVE_POLL)
-#    define SOCKET_HAVE_POLL
-#  endif
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
-#  if !defined(SOCKET_HAVE_KQUEUE)
-#    define SOCKET_HAVE_KQUEUE
-#  endif
-#  if !defined(SOCKET_HAVE_POLL)
-#    define SOCKET_HAVE_POLL
-#  endif
-#elif defined(__linux__)
-#  if !defined(SOCKET_HAVE_EPOLL)
-#    define SOCKET_HAVE_EPOLL
-#  endif
-#  if !defined(SOCKET_HAVE_POLL)
-#    define SOCKET_HAVE_POLL
-#  endif
-#endif
-
-/*
- * Define SOCKET_DEFAULT_BACKEND
- * ------------------------------------------------------------------
- */
-
-/**
- * Defines the default Listener backend to use.
- *
- * @note Do not rely on the value shown in doxygen.
- */
-#if defined(_WIN32)
-#  if !defined(SOCKET_DEFAULT_BACKEND)
-#    if defined(SOCKET_HAVE_POLL)
-#      define SOCKET_DEFAULT_BACKEND Poll
-#    else
-#      define SOCKET_DEFAULT_BACKEND Select
-#    endif
-#  endif
-#elif defined(__linux__)
-#  include <sys/epoll.h>
-
-#  if !defined(SOCKET_DEFAULT_BACKEND)
-#    define SOCKET_DEFAULT_BACKEND Epoll
-#  endif
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__)
-#  include <sys/types.h>
-#  include <sys/event.h>
-#  include <sys/time.h>
-
-#  if !defined(SOCKET_DEFAULT_BACKEND)
-#    define SOCKET_DEFAULT_BACKEND Kqueue
-#  endif
-#else
-#  if !defined(SOCKET_DEFAULT_BACKEND)
-#    define SOCKET_DEFAULT_BACKEND Select
-#  endif
-#endif
-
-#if defined(SOCKET_HAVE_POLL) && !defined(_WIN32)
-#  include <poll.h>
-#endif
-
-/*
- * Headers to include
- * ------------------------------------------------------------------
- */
-
-#if defined(_WIN32)
-#  include <cstdlib>
-
-#  include <WinSock2.h>
-#  include <WS2tcpip.h>
-#else
-#  include <cerrno>
-
-#  include <sys/ioctl.h>
-#  include <sys/types.h>
-#  include <sys/socket.h>
-#  include <sys/un.h>
-
-#  include <arpa/inet.h>
-
-#  include <netinet/in.h>
-#  include <netinet/tcp.h>
-
-#  include <fcntl.h>
-#  include <netdb.h>
-#  include <unistd.h>
-#endif
-
-#if !defined(SOCKET_NO_SSL)
-#  include <openssl/err.h>
-#  include <openssl/evp.h>
-#  include <openssl/ssl.h>
-#endif
-
-#include <cassert>
-#include <chrono>
-#include <cstdlib>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * General network namespace.
- */
-namespace net {
-
-/*
- * Portables types
- * ------------------------------------------------------------------
- *
- * The following types are defined differently between Unix and Windows.
- */
-
-/* {{{ Protocols */
-
-#if defined(_WIN32)
-
-/**
- * Socket type, SOCKET.
- */
-using Handle	= SOCKET;
-
-/**
- * Argument to pass to set.
- */
-using ConstArg	= const char *;
-
-/**
- * Argument to pass to get.
- */
-using Arg	= char *;
-
-#else
-
-/**
- * Socket type, int.
- */
-using Handle	= int;
-
-/**
- * Argument to pass to set.
- */
-using ConstArg	= const void *;
-
-/**
- * Argument to pass to get.
- */
-using Arg	= void *;
-
-#endif
-
-/* }}} */
-
-/*
- * Portable constants
- * ------------------------------------------------------------------
- *
- * These constants are needed to check functions return codes, they are rarely needed in end user code.
- */
-
-/* {{{ Constants */
-
-/*
- * The following constants are defined differently from Unix
- * to Windows.
- */
-#if defined(_WIN32)
-
-/**
- * Socket creation failure or invalidation.
- */
-extern const Handle Invalid;
-
-/**
- * Socket operation failure.
- */
-extern const int Failure;
-
-#else
-
-/**
- * Socket creation failure or invalidation.
- */
-extern const int Invalid;
-
-/**
- * Socket operation failure.
- */
-extern const int Failure;
-
-#endif
-
-#if !defined(SOCKET_NO_SSL)
-
-namespace ssl {
-
-/**
- * @enum Method
- * @brief Which OpenSSL method to use.
- */
-enum Method {
-	Tlsv1,		//!< TLS v1.2 (recommended)
-	Sslv3		//!< SSLv3
-};
-
-} // !ssl
-
-#endif
-
-/* }}} */
-
-/*
- * Portable functions
- * ------------------------------------------------------------------
- *
- * The following free functions can be used to initialize the library or to get the last system error.
- */
-
-/* {{{ Functions */
-
-/**
- * Initialize the socket library. Except if you defined SOCKET_NO_AUTO_INIT, you don't need to call this
- * function manually.
- */
-void init() noexcept;
-
-/**
- * Close the socket library.
- */
-void finish() noexcept;
-
-#if !defined(SOCKET_NO_SSL)
-
-/**
- * OpenSSL namespace.
- */
-namespace ssl {
-
-/**
- * Initialize the OpenSSL library. Except if you defined SOCKET_NO_AUTO_SSL_INIT, you don't need to call this function
- * manually.
- */
-void init() noexcept;
-
-/**
- * Close the OpenSSL library.
- */
-void finish() noexcept;
-
-} // !ssl
-
-#endif // SOCKET_NO_SSL
-
-/**
- * Get the last socket system error. The error is set from errno or from
- * WSAGetLastError on Windows.
- *
- * @return a string message
- */
-std::string error();
-
-/**
- * Get the last system error.
- *
- * @param errn the error number (errno or WSAGetLastError)
- * @return the error
- */
-std::string error(int errn);
-
-/* }}} */
-
-/*
- * Error class
- * ------------------------------------------------------------------
- *
- * This is the main exception thrown on socket operations.
- */
-
-/* {{{ Error */
-
-/**
- * @class Error
- * @brief Base class for sockets error
- */
-class Error : public std::exception {
-public:
-	/**
-	 * @enum Code
-	 * @brief Which kind of error
-	 */
-	enum Code {
-		Timeout,		///!< The action did timeout
-		System,			///!< There is a system error
-		Other			///!< Other custom error
-	};
-
-private:
-	Code m_code;
-	std::string m_function;
-	std::string m_error;
-
-public:
-	/**
-	 * Constructor that use the last system error.
-	 *
-	 * @param code which kind of error
-	 * @param function the function name
-	 */
-	Error(Code code, std::string function);
-
-	/**
-	 * Constructor that use the system error set by the user.
-	 *
-	 * @param code which kind of error
-	 * @param function the function name
-	 * @param error the error
-	 */
-	Error(Code code, std::string function, int error);
-
-	/**
-	 * Constructor that set the error specified by the user.
-	 *
-	 * @param code which kind of error
-	 * @param function the function name
-	 * @param error the error
-	 */
-	Error(Code code, std::string function, std::string error);
-
-	/**
-	 * Get which function has triggered the error.
-	 *
-	 * @return the function name (e.g connect)
-	 */
-	inline const std::string &function() const noexcept
-	{
-		return m_function;
-	}
-
-	/**
-	 * The error code.
-	 *
-	 * @return the code
-	 */
-	inline Code code() const noexcept
-	{
-		return m_code;
-	}
-
-	/**
-	 * Get the error (only the error content).
-	 *
-	 * @return the error
-	 */
-	const char *what() const noexcept
-	{
-		return m_error.c_str();
-	}
-};
-
-/* }}} */
-
-/*
- * State class
- * ------------------------------------------------------------------
- *
- * To facilitate higher-level stuff, the socket has a state.
- */
-
-/* {{{ State */
-
-/**
- * @enum State
- * @brief Current socket state.
- */
-enum class State {
-	Open,			//!< Socket is open
-	Bound,			//!< Socket is bound to an address
-	Connecting,		//!< The connection is in progress
-	Connected,		//!< Connection is complete
-	Accepted,		//!< Socket has been accepted (client)
-	Accepting,		//!< The client acceptation is in progress
-	Closed,			//!< The socket has been closed
-	Disconnected,		//!< The connection was lost
-};
-
-/* }}} */
-
-/*
- * Action enum
- * ------------------------------------------------------------------
- *
- * Defines the pending operation.
- */
-
-/* {{{ Action */
-
-/**
- * @enum Action
- * @brief Define the current operation that must complete.
- *
- * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket
- * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the
- * operation is complete.
- *
- * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is
- * met.
- *
- * It is important to complete the operation in the correct order because protocols like Tls may require to continue
- * re-negociating when calling Socket::send or Socket::Recv.
- */
-enum class Action {
-	None,		//!< No action is required, socket is ready
-	Accept,		//!< The socket is not yet accepted, caller must call accept() again
-	Connect,	//!< The socket is not yet connected, caller must call connect() again
-	Receive,	//!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again
-	Send		//!< The send operation has not succeded yet, caller must call send() or sendto() again
-};
-
-/* }}} */
-
-/*
- * Condition enum
- * ------------------------------------------------------------------
- *
- * Defines if we must wait for reading or writing.
- */
-
-/* {{{ Condition */
-
-/**
- * @enum Condition
- * @brief Define the required condition for the socket.
- *
- * As explained in Action enumeration, some operations required to be called several times, before calling these
- * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition.
- */
-enum class Condition {
-	None,			//!< No condition is required
-	Readable = (1 << 0),	//!< The socket must be readable
-	Writable = (1 << 1)	//!< The socket must be writable
-};
-
-/**
- * Apply bitwise XOR.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-constexpr Condition operator^(Condition v1, Condition v2) noexcept
-{
-	return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
-}
-
-/**
- * Apply bitwise AND.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-constexpr Condition operator&(Condition v1, Condition v2) noexcept
-{
-	return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
-}
-
-/**
- * Apply bitwise OR.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-constexpr Condition operator|(Condition v1, Condition v2) noexcept
-{
-	return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
-}
-
-/**
- * Apply bitwise NOT.
- *
- * @param v the value
- * @return the complement
- */
-constexpr Condition operator~(Condition v) noexcept
-{
-	return static_cast<Condition>(~static_cast<int>(v));
-}
-
-/**
- * Assign bitwise OR.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-inline Condition &operator|=(Condition &v1, Condition v2) noexcept
-{
-	v1 = static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
-
-	return v1;
-}
-
-/**
- * Assign bitwise AND.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-inline Condition &operator&=(Condition &v1, Condition v2) noexcept
-{
-	v1 = static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
-
-	return v1;
-}
-
-/**
- * Assign bitwise XOR.
- *
- * @param v1 the first value
- * @param v2 the second value
- * @return the new value
- */
-inline Condition &operator^=(Condition &v1, Condition v2) noexcept
-{
-	v1 = static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
-
-	return v1;
-}
-
-/* }}} */
-
-/*
- * Base Socket class
- * ------------------------------------------------------------------
- *
- * This base class has operations that are common to all types of sockets but you usually instanciate
- * a SocketTcp or SocketUdp
- */
-
-/* {{{ Socket */
-
-/**
- * @class Socket
- * @brief Base socket class for socket operations.
- *
- * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the
- * underlying protocol for more details.
- *
- * @see protocol::Tls
- * @see protocol::Tcp
- * @see protocol::Udp
- */
-template <typename Address, typename Protocol>
-class Socket {
-private:
-	Protocol m_proto;
-	State m_state{State::Closed};
-	Action m_action{Action::None};
-	Condition m_condition{Condition::None};
-
-protected:
-	/**
-	 * The native handle.
-	 */
-	Handle m_handle{Invalid};
-
-public:
-	/**
-	 * Create a socket handle.
-	 *
-	 * This is the primary function and the only one that creates the socket handle, all other constructors
-	 * are just overloaded functions.
-	 *
-	 * @param domain the domain AF_*
-	 * @param type the type SOCK_*
-	 * @param protocol the protocol
-	 * @param iface the implementation
-	 * @throw net::Error on errors
-	 * @post state is set to Open
-	 * @post handle is not set to Invalid
-	 */
-	Socket(int domain, int type, int protocol, Protocol iface = {})
-		: m_proto(std::move(iface))
-	{
-#if !defined(SOCKET_NO_AUTO_INIT)
-		init();
-#endif
-		m_handle = ::socket(domain, type, protocol);
-
-		if (m_handle == Invalid) {
-			throw Error{Error::System, "socket"};
-		}
-
-		m_proto.create(*this);
-		m_state = State::Open;
-
-		assert(m_handle != Invalid);
-	}
-
-	/**
-	 * This tries to create a socket.
-	 *
-	 * Domain and type are determined by the Address and Protocol object.
-	 *
-	 * @param protocol the protocol
-	 * @param address which type of address
-	 * @throw net::Error on errors
-	 */
-	explicit inline Socket(Protocol protocol = {}, const Address &address = {})
-		: Socket{address.domain(), protocol.type(), 0, std::move(protocol)}
-	{
-	}
-
-	/**
-	 * Construct a socket with an already created descriptor.
-	 *
-	 * @param handle the native descriptor
-	 * @param state specify the socket state
-	 * @param protocol the type of socket implementation
-	 * @post action is set to None
-	 * @post condition is set to None
-	 */
-	explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept
-		: m_proto(std::move(protocol))
-		, m_state{state}
-		, m_handle{handle}
-	{
-		assert(m_action == Action::None);
-		assert(m_condition == Condition::None);
-	}
-
-	/**
-	 * Create an invalid socket. Can be used when you cannot instanciate the socket immediately.
-	 */
-	explicit inline Socket(std::nullptr_t) noexcept
-		: m_handle{Invalid}
-	{
-	}
-
-	/**
-	 * Copy constructor deleted.
-	 */
-	Socket(const Socket &) = delete;
-
-	/**
-	 * Transfer ownership from other to this.
-	 *
-	 * @param other the other socket
-	 */
-	inline Socket(Socket &&other) noexcept
-		: m_proto(std::move(other.m_proto))
-		, m_state{other.m_state}
-		, m_action{other.m_action}
-		, m_condition{other.m_condition}
-		, m_handle{other.m_handle}
-	{
-		/* Invalidate other */
-		other.m_handle = Invalid;
-		other.m_state = State::Closed;
-		other.m_action = Action::None;
-		other.m_condition = Condition::None;
-	}
-
-	/**
-	 * Default destructor.
-	 */
-	virtual ~Socket()
-	{
-		close();
-	}
-
-	/**
-	 * Access the implementation.
-	 *
-	 * @return the implementation
-	 * @warning use this function with care
-	 */
-	inline const Protocol &protocol() const noexcept
-	{
-		return m_proto;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the implementation
-	 */
-	inline Protocol &protocol() noexcept
-	{
-		return m_proto;
-	}
-
-	/**
-	 * Get the current socket state.
-	 *
-	 * @return the state
-	 */
-	inline State state() const noexcept
-	{
-		return m_state;
-	}
-
-	/**
-	 * Change the current socket state.
-	 *
-	 * @param state the new state
-	 * @warning only implementations should call this function
-	 */
-	inline void setState(State state) noexcept
-	{
-		m_state = state;
-	}
-
-	/**
-	 * Get the pending operation.
-	 *
-	 * @return the action to complete before continuing
-	 * @note usually only needed in non-blocking sockets
-	 */
-	inline Action action() const noexcept
-	{
-		return m_action;
-	}
-
-	/**
-	 * Change the pending operation.
-	 *
-	 * @param action the action
-	 * @warning you should not call this function yourself
-	 */
-	inline void setAction(Action action) noexcept
-	{
-		m_action = action;
-	}
-
-	/**
-	 * Get the condition to wait for.
-	 *
-	 * @return the condition
-	 */
-	inline Condition condition() const noexcept
-	{
-		return m_condition;
-	}
-
-	/**
-	 * Change the condition required.
-	 *
-	 * @param condition the condition
-	 * @warning you should not call this function yourself
-	 */
-	inline void setCondition(Condition condition) noexcept
-	{
-		m_condition = condition;
-	}
-
-	/**
-	 * Set an option for the socket. Wrapper of setsockopt(2).
-	 *
-	 * @param level the setting level
-	 * @param name the name
-	 * @param arg the value
-	 * @throw net::Error on errors
-	 */
-	template <typename Argument>
-	void set(int level, int name, const Argument &arg)
-	{
-		if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) {
-			throw Error{Error::System, "set"};
-		}
-	}
-
-	/**
-	 * Object-oriented option setter.
-	 *
-	 * The object must have `set(Socket<Address, Protocol> &) const`.
-	 *
-	 * @param option the option
-	 * @throw net::Error on errors
-	 */
-	template <typename Option>
-	inline void set(const Option &option)
-	{
-		option.set(*this);
-	}
-
-	/**
-	 * Get an option for the socket. Wrapper of getsockopt(2).
-	 *
-	 * @param level the setting level
-	 * @param name the name
-	 * @throw net::Error on errors
-	 */
-	template <typename Argument>
-	Argument get(int level, int name)
-	{
-		Argument desired, result{};
-		socklen_t size = sizeof (result);
-
-		if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) {
-			throw Error{Error::System, "get"};
-		}
-
-		std::memcpy(&result, &desired, size);
-
-		return result;
-	}
-
-	/**
-	 * Object-oriented option getter.
-	 *
-	 * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value
-	 * returned from this function.
-	 *
-	 * @return the same value as get() in the option
-	 * @throw net::Error on errors
-	 */
-	template <typename Option>
-	inline auto get() -> decltype(std::declval<Option>().get(*this))
-	{
-		return Option{}.get(*this);
-	}
-
-	/**
-	 * Get the native handle.
-	 *
-	 * @return the handle
-	 * @warning Not portable
-	 */
-	inline Handle handle() const noexcept
-	{
-		return m_handle;
-	}
-
-	/**
-	 * Bind using a native address.
-	 *
-	 * @param address the address
-	 * @param length the size
-	 * @pre state must not be Bound
-	 * @throw net::Error on errors
-	 */
-	void bind(const sockaddr *address, socklen_t length)
-	{
-		assert(m_state != State::Bound);
-
-		if (::bind(m_handle, address, length) == Failure) {
-			throw Error{Error::System, "bind"};
-		}
-
-		m_state = State::Bound;
-	}
-
-	/**
-	 * Overload that takes an address.
-	 *
-	 * @param address the address
-	 * @throw net::Error on errors
-	 */
-	inline void bind(const Address &address)
-	{
-		bind(address.address(), address.length());
-	}
-
-	/**
-	 * Listen for pending connection.
-	 *
-	 * @param max the maximum number
-	 * @pre state must be Bound
-	 * @throw net::Error on errors
-	 */
-	inline void listen(int max = 128)
-	{
-		assert(m_state == State::Bound);
-
-		if (::listen(this->m_handle, max) == Failure) {
-			throw Error{Error::System, "listen"};
-		}
-	}
-
-	/**
-	 * Connect to the address.
-	 *
-	 * If connection cannot be established immediately, connect with no argument must be called again. See
-	 * the underlying protocol for more information.
-	 *
-	 * @pre state must be State::Open
-	 * @param address the address
-	 * @param length the the address length
-	 * @throw net::Error on errors
-	 * @post state is set to State::Connecting or State::Connected
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	void connect(const sockaddr *address, socklen_t length)
-	{
-		assert(m_state == State::Open);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.connect(*this, address, length);
-
-		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
-		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * Effectively call connect(address.address(), address.length());
-	 *
-	 * @param address the address
-	 */
-	inline void connect(const Address &address)
-	{
-		connect(address.address(), address.length());
-	}
-
-	/**
-	 * Continue the connection, only required with non-blocking sockets.
-	 *
-	 * @pre state must be State::Connecting
-	 * @throw net::Error on errors
-	 */
-	void connect()
-	{
-		assert(m_state == State::Connecting);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.connect(*this);
-
-		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
-		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
-	}
-
-	/**
-	 * Accept a new client. If there are no pending connection, throws an error.
-	 *
-	 * If the client cannot be accepted immediately, the client is returned and accept with no arguments
-	 * must be called on it. See the underlying protocol for more information.
-	 *
-	 * @pre state must be State::Bound
-	 * @param info the address where to store client's information (optional)
-	 * @return the new socket
-	 * @throw Error on errors
-	 * @post returned client's state is set to State::Accepting or State::Accepted
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	Socket<Address, Protocol> accept(Address *info)
-	{
-		assert(m_state == State::Bound);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		sockaddr_storage storage;
-		socklen_t length = sizeof (storage);
-
-		Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length);
-
-		if (info) {
-			*info = Address{&storage, length};
-		}
-
-		/* Master do not change */
-		assert(m_state == State::Bound);
-		assert(m_action == Action::None);
-		assert(m_condition == Condition::None);
-
-		/* Client */
-		assert(
-			(sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) ||
-			(sc.state() == State::Accepted  && sc.action() == Action::None   && sc.condition() == Condition::None)
-		);
-
-		return sc;
-	}
-
-	/**
-	 * Continue the accept process on this client. This function must be called only when the socket is
-	 * ready to be readable or writable! (see condition).
-	 *
-	 * @pre state must be State::Accepting
-	 * @throw Error on errors
-	 * @post if connection is complete, state is changed to State::Accepted, action and condition are unset
-	 * @post if connection is still in progress, condition is set
-	 */
-	void accept()
-	{
-		assert(m_state == State::Accepting);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.accept(*this);
-
-		assert(
-			(m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) ||
-			(m_state == State::Accepted  && m_action == Action::None   && m_condition == Condition::None)
-		);
-	}
-
-	/**
-	 * Get the local name. This is a wrapper of getsockname().
-	 *
-	 * @return the address
-	 * @throw Error on failures
-	 * @pre state() must not be State::Closed
-	 */
-	Address address() const
-	{
-		assert(m_state != State::Closed);
-
-		sockaddr_storage ss;
-		socklen_t length = sizeof (sockaddr_storage);
-
-		if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) {
-			throw Error{Error::System, "getsockname"};
-		}
-
-		return Address(&ss, length);
-	}
-
-	/**
-	 * Receive some data.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * If action is set to Action::None and result is set to 0, disconnection occured.
-	 *
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @pre action must not be Action::Send
-	 * @return the number of bytes received or 0
-	 * @throw Error on error
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	unsigned recv(void *data, unsigned length)
-	{
-		assert(m_action != Action::Send);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		unsigned nbread = m_proto.recv(*this, data, length);
-
-		return nbread;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param count the number of bytes to receive
-	 * @return the string
-	 * @throw Error on error
-	 */
-	inline std::string recv(unsigned count)
-	{
-		std::string result;
-
-		result.resize(count);
-		auto n = recv(const_cast<char *>(result.data()), count);
-		result.resize(n);
-
-		return result;
-	}
-
-	/**
-	 * Send some data.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * @param data the data buffer
-	 * @param length the buffer length
-	 * @return the number of bytes sent or 0
-	 * @pre action() must not be Flag::Receive
-	 * @throw Error on error
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	unsigned send(const void *data, unsigned length)
-	{
-		assert(m_action != Action::Receive);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		unsigned nbsent = m_proto.send(*this, data, length);
-
-		assert((m_action == Action::None && m_condition == Condition::None) ||
-		       (m_action == Action::Send && m_condition != Condition::None));
-
-		return nbsent;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the string to send
-	 * @return the number of bytes sent
-	 * @throw Error on error
-	 */
-	inline unsigned send(const std::string &data)
-	{
-		return send(data.c_str(), data.size());
-	}
-
-	/**
-	 * Send data to an end point.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the client address
-	 * @param addrlen the address length
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
-	{
-		return m_proto.sendto(*this, data, length, address, addrlen);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the destination
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 */
-	inline unsigned sendto(const void *data, unsigned length, const Address &address)
-	{
-		return sendto(data, length, address.address(), address.length());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the data
-	 * @param address the address
-	 * @return the number of bytes sent
-	 * @throw net:;Error on errors
-	 */
-	inline unsigned sendto(const std::string &data, const Address &address)
-	{
-		return sendto(data.c_str(), data.length(), address);
-	}
-
-	/**
-	 * Receive data from an end point.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param address the address destination
-	 * @param addrlen the address length (in/out)
-	 * @return the number of bytes received
-	 * @throw net::Error on errors
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
-	{
-		return m_proto.recvfrom(*this, data, length, address, addrlen);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param info the address destination
-	 * @return the number of bytes received
-	 * @throw net::Error on errors
-	 */
-	inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr)
-	{
-		sockaddr_storage storage;
-		socklen_t addrlen = sizeof (sockaddr_storage);
-
-		auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen);
-
-		if (info && n != 0) {
-			*info = Address{&storage, addrlen};
-		}
-
-		return n;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param count the maximum number of bytes to receive
-	 * @param info the client information
-	 * @return the string
-	 * @throw net::Error on errors
-	 */
-	std::string recvfrom(unsigned count, Address *info = nullptr)
-	{
-		std::string result;
-
-		result.resize(count);
-		auto n = recvfrom(const_cast<char *>(result.data()), count, info);
-		result.resize(n);
-
-		return result;
-	}
-
-	/**
-	 * Close the socket.
-	 *
-	 * Automatically called from the destructor.
-	 */
-	void close()
-	{
-		if (m_handle != Invalid) {
-#if defined(_WIN32)
-			::closesocket(m_handle);
-#else
-			::close(m_handle);
-#endif
-			m_handle = Invalid;
-		}
-
-		m_state = State::Closed;
-		m_action = Action::None;
-		m_condition = Condition::None;
-	}
-
-	/**
-	 * Assignment operator forbidden.
-	 *
-	 * @return *this
-	 */
-	Socket &operator=(const Socket &) = delete;
-
-	/**
-	 * Transfer ownership from other to this. The other socket is left
-	 * invalid and will not be closed.
-	 *
-	 * @param other the other socket
-	 * @return this
-	 */
-	Socket &operator=(Socket &&other) noexcept
-	{
-		m_handle = other.m_handle;
-		m_proto = std::move(other.m_proto);
-		m_state = other.m_state;
-		m_action = other.m_action;
-		m_condition = other.m_condition;
-
-		/* Invalidate other */
-		other.m_handle = Invalid;
-		other.m_state = State::Closed;
-		other.m_action = Action::None;
-		other.m_condition = Condition::None;
-
-		return *this;
-	}
-};
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if they equals
- */
-template <typename Address, typename Protocol>
-bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() == s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if they are different
- */
-template <typename Address, typename Protocol>
-bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() != s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if s1 < s2
- */
-template <typename Address, typename Protocol>
-bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() < s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if s1 > s2
- */
-template <typename Address, typename Protocol>
-bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() > s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if s1 <= s2
- */
-template <typename Address, typename Protocol>
-bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() <= s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * @param s1 the first socket
- * @param s2 the second socket
- * @return true if s1 >= s2
- */
-template <typename Address, typename Protocol>
-bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-	return s1.handle() >= s2.handle();
-}
-
-/* }}} */
-
-/*
- * Predefined options
- * ------------------------------------------------------------------
- */
-
-/* {{{ Options */
-
-/**
- * Namespace of predefined options.
- */
-namespace option {
-
-/*
- * Options for socket
- * ------------------------------------------------------------------
- */
-
-/* {{{ Options for socket */
-
-/**
- * @class SockBlockMode
- * @brief Set or get the blocking-mode for a socket.
- * @warning On Windows, it's not possible to check if the socket is blocking or not.
- */
-class SockBlockMode {
-public:
-	/**
-	 * Set to false if you want non-blocking socket.
-	 */
-	bool value{false};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	void set(Socket<Address, Protocol> &sc) const
-	{
-#if defined(O_NONBLOCK) && !defined(_WIN32)
-		int flags;
-
-		if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) {
-			flags = 0;
-		}
-
-		if (value) {
-			flags &= ~(O_NONBLOCK);
-		} else {
-			flags |= O_NONBLOCK;
-		}
-
-		if (fcntl(sc.handle(), F_SETFL, flags) < 0) {
-			throw Error{Error::System, "fcntl"};
-		}
-#else
-		unsigned long flags = (value) ? 0 : 1;
-
-		if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) {
-			throw Error{Error::System, "fcntl"};
-		}
-#endif
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	bool get(Socket<Address, Protocol> &sc) const
-	{
-#if defined(O_NONBLOCK) && !defined(_WIN32)
-		int flags = fcntl(sc.handle(), F_GETFL, 0);
-
-		if (flags < 0) {
-			throw Error{Error::System, "fcntl"};
-		}
-
-		return !(flags & O_NONBLOCK);
-#else
-		throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"};
-#endif
-	}
-};
-
-/**
- * @class SockReuseAddress
- * @brief Reuse address, must be used before calling Socket::bind
- */
-class SockReuseAddress {
-public:
-	/**
-	 * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option.
-	 */
-	bool value{true};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void set(Socket<Address, Protocol> &sc) const
-	{
-		sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0);
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline bool get(Socket<Address, Protocol> &sc) const
-	{
-		return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR));
-	}
-};
-
-/**
- * @class SockSendBuffer
- * @brief Set or get the output buffer.
- */
-class SockSendBuffer {
-public:
-	/**
-	 * Set to the buffer size.
-	 */
-	int value{2048};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void set(Socket<Address, Protocol> &sc) const
-	{
-		sc.set(SOL_SOCKET, SO_SNDBUF, value);
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline int get(Socket<Address, Protocol> &sc) const
-	{
-		return sc.template get<int>(SOL_SOCKET, SO_SNDBUF);
-	}
-};
-
-/**
- * @class SockReceiveBuffer
- * @brief Set or get the input buffer.
- */
-class SockReceiveBuffer {
-public:
-	/**
-	 * Set to the buffer size.
-	 */
-	int value{2048};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void set(Socket<Address, Protocol> &sc) const
-	{
-		sc.set(SOL_SOCKET, SO_RCVBUF, value);
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline int get(Socket<Address, Protocol> &sc) const
-	{
-		return sc.template get<int>(SOL_SOCKET, SO_RCVBUF);
-	}
-};
-
-/* }}} */
-
-/**
- * @class TcpNoDelay
- * @brief Set this option if you want to disable nagle's algorithm.
- */
-class TcpNoDelay {
-public:
-	/**
-	 * Set to true to set TCP_NODELAY option.
-	 */
-	bool value{true};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void set(Socket<Address, Protocol> &sc) const
-	{
-		sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0);
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline bool get(Socket<Address, Protocol> &sc) const
-	{
-		return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY));
-	}
-};
-
-/**
- * @class Ipv6Only
- * @brief Control IPPROTO_IPV6/IPV6_V6ONLY
- *
- * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either
- * false or true if portability is a concern.
- */
-class Ipv6Only {
-public:
-	/**
-	 * Set this to use only IPv6.
-	 */
-	bool value{true};
-
-	/**
-	 * Set the option.
-	 *
-	 * @param sc the socket
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void set(Socket<Address, Protocol> &sc) const
-	{
-		sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0);
-	}
-
-	/**
-	 * Get the option.
-	 *
-	 * @return the value
-	 * @throw Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline bool get(Socket<Address, Protocol> &sc) const
-	{
-		return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY));
-	}
-};
-
-} // !option
-
-/* }}} */
-
-/*
- * Predefined addressed to be used
- * ------------------------------------------------------------------
- *
- * - Ip,
- * - Local.
- */
-
-/* {{{ Addresses */
-
-/**
- * Set of predefined addresses.
- */
-namespace address {
-
-/**
- * @class Ip
- * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4.
- */
-class Ip {
-public:
-	/**
-	 * @enum Type
-	 * @brief Type of ip address.
-	 */
-	enum Type {
-		v4 = AF_INET,		//!< AF_INET
-		v6 = AF_INET6		//!< AF_INET6
-	};
-
-private:
-	/*
-	 * Default domain when using default constructors.
-	 *
-	 * Note: AF_INET or AF_INET6, not
-	 */
-	static int m_default;
-
-	union {
-		sockaddr_in m_sin;
-		sockaddr_in6 m_sin6;
-	};
-
-	socklen_t m_length{0};
-	int m_domain{AF_INET};
-
-public:
-	/**
-	 * Set the default domain to use when using default Ip constructor. By default, AF_INET is used.
-	 *
-	 * @pre domain must be Type::v4 or Type::v6
-	 */
-	static inline void setDefault(Type domain) noexcept
-	{
-		assert(domain == Type::v4 || domain == Type::v6);
-
-		m_default = static_cast<int>(domain);
-	}
-
-	/**
-	 * Construct using the default domain.
-	 */
-	inline Ip() noexcept
-		: Ip(static_cast<Type>(m_default))
-	{
-	}
-
-	/**
-	 * Default initialize the Ip domain.
-	 *
-	 * @pre domain must be AF_INET or AF_INET6 only
-	 * @param domain the domain (AF_INET or AF_INET6)
-	 */
-	Ip(Type domain) noexcept;
-
-	/**
-	 * Construct an address suitable for bind() or connect().
-	 *
-	 * @pre domain must be Type::v4 or Type::v6
-	 * @param domain the domain (AF_INET or AF_INET6)
-	 * @param host the host (* for any)
-	 * @param port the port number
-	 * @throw Error on errors
-	 */
-	Ip(const std::string &host, int port, Type domain = v4);
-
-	/**
-	 * Construct an address from a storage.
-	 *
-	 * @pre storage's domain must be AF_INET or AF_INET6 only
-	 * @param ss the storage
-	 * @param length the length
-	 */
-	Ip(const sockaddr_storage *ss, socklen_t length) noexcept;
-
-	/**
-	 * Get the domain (AF_INET or AF_INET6).
-	 *
-	 * @return the domain
-	 */
-	inline int domain() const noexcept
-	{
-		return m_domain;
-	}
-
-	/**
-	 * Return the underlying address, either sockaddr_in6 or sockaddr_in.
-	 *
-	 * @return the address
-	 */
-	inline const sockaddr *address() const noexcept
-	{
-		if (m_domain == AF_INET6) {
-			return reinterpret_cast<const sockaddr *>(&m_sin6);
-		}
-
-		return reinterpret_cast<const sockaddr *>(&m_sin);
-	}
-
-	/**
-	 * Return the underlying address length.
-	 *
-	 * @return the length
-	 */
-	inline socklen_t length() const noexcept
-	{
-		return m_length;
-	}
-
-	/**
-	 * Get the port.
-	 *
-	 * @return the port
-	 */
-	inline int port() const noexcept
-	{
-		if (m_domain == AF_INET6) {
-			return ntohs(m_sin6.sin6_port);
-		}
-
-		return ntohs(m_sin.sin_port);
-	}
-};
-
-#if !defined(_WIN32)
-
-/**
- * @class Local
- * @brief unix family sockets
- *
- * Create an address to a specific path. Only available on Unix.
- */
-class Local {
-private:
-	sockaddr_un m_sun;
-	std::string m_path;
-
-public:
-	/**
-	 * Get the domain AF_LOCAL.
-	 *
-	 * @return AF_LOCAL
-	 */
-	inline int domain() const noexcept
-	{
-		return AF_LOCAL;
-	}
-
-	/**
-	 * Default constructor.
-	 */
-	Local() noexcept;
-
-	/**
-	 * Construct an address to a path.
-	 *
-	 * @param path the path
-	 * @param rm remove the file before (default: false)
-	 */
-	Local(std::string path, bool rm = false) noexcept;
-
-	/**
-	 * Construct an unix address from a storage address.
-	 *
-	 * @pre storage's domain must be AF_LOCAL
-	 * @param ss the storage
-	 * @param length the length
-	 */
-	Local(const sockaddr_storage *ss, socklen_t length) noexcept;
-
-	/**
-	 * Get the sockaddr_un.
-	 *
-	 * @return the address
-	 */
-	inline const sockaddr *address() const noexcept
-	{
-		return reinterpret_cast<const sockaddr *>(&m_sun);
-	}
-
-	/**
-	 * Get the address length.
-	 *
-	 * @return the length
-	 */
-	inline socklen_t length() const noexcept
-	{
-#if defined(SOCKET_HAVE_SUN_LEN)
-		return SUN_LEN(&m_sun);
-#else
-		return sizeof (m_sun);
-#endif
-	}
-};
-
-#endif // !_WIN32
-
-} // !address
-
-/* }}} */
-
-/*
- * Predefined protocols
- * ------------------------------------------------------------------
- *
- * - Tcp, for standard stream connections,
- * - Udp, for standard datagram connections,
- * - Tls, for secure stream connections.
- */
-
-/* {{{ Protocols */
-
-/**
- * Set of predefined protocols.
- */
-namespace protocol {
-
-/* {{{ Tcp */
-
-/**
- * @class Tcp
- * @brief Clear TCP implementation.
- *
- * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual
- * C functions.
- */
-class Tcp {
-public:
-	/**
-	 * Socket type.
-	 *
-	 * @return SOCK_STREAM
-	 */
-	inline int type() const noexcept
-	{
-		return SOCK_STREAM;
-	}
-
-	/**
-	 * Do nothing.
-	 *
-	 * This function is just present for compatibility, it should never be called.
-	 */
-	template <typename Address>
-	inline void create(Socket<Address, Tcp> &) const noexcept
-	{
-		/* No-op */
-	}
-
-	/**
-	 * Standard connect.
-	 *
-	 * If the socket is marked non-blocking and the connection cannot be established immediately, then the
-	 * following is true:
-	 *
-	 * - state is set to State::Connecting,
-	 * - action is set to Action::Connect,
-	 * - condition is set to Condition::Writable.
-	 *
-	 * Then the user must wait until the socket is writable and call connect() with 0 arguments.
-	 *
-	 * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in
-	 * that case state is either set to State::Connected or State::Disconnected but action and condition are
-	 * not set.
-	 *
-	 * @param sc the socket
-	 * @param address the address
-	 * @param length the length
-	 * @throw net::Error on errors
-	 * @note Wrapper of connect(2)
-	 */
-	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length)
-	{
-		if (::connect(sc.handle(), address, length) == Failure) {
-			/*
-			 * Determine if the error comes from a non-blocking connect that cannot be
-			 * accomplished yet.
-			 */
-#if defined(_WIN32)
-			int error = WSAGetLastError();
-
-			if (error == WSAEWOULDBLOCK) {
-				sc.setState(State::Connecting);
-				sc.setAction(Action::Connect);
-				sc.setCondition(Condition::Writable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "connect", error};
-			}
-#else
-			if (errno == EINPROGRESS) {
-				sc.setState(State::Connecting);
-				sc.setAction(Action::Connect);
-				sc.setCondition(Condition::Writable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "connect"};
-			}
-#endif
-		} else {
-			sc.setState(State::Connected);
-		}
-	}
-
-	/**
-	 * Continue the connection. This function must only be called when the socket is ready for writing,
-	 * the user is responsible of waiting for that condition.
-	 *
-	 * This function check for SOL_SOCKET/SO_ERROR status.
-	 *
-	 * If the connection is complete, status is set to State::Connected, otherwise it is set to
-	 * State::Disconnected. In both cases, action and condition are not set.
-	 *
-	 * @param sc the socket
-	 * @throw net::Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc)
-	{
-		int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
-
-		if (error == Failure) {
-			sc.setState(State::Disconnected);
-			throw Error{Error::System, "connect", error};
-		}
-
-		sc.setState(State::Connected);
-	}
-
-	/**
-	 * Accept a clear client.
-	 *
-	 * If the socket is marked non-blocking and there are no pending connection, this function throws an
-	 * error. The user must wait that the socket is readable before calling this function.
-	 *
-	 * If the socket is blocking, this function blocks until a new client is connected or throws an error on
-	 * errors.
-	 *
-	 * If the socket is correctly returned, its state is set to State::Accepted and its action and condition
-	 * are not set.
-	 *
-	 * In any case, action and condition of this socket are not set.
-	 *
-	 * @param sc the socket
-	 * @param address the address destination
-	 * @param length the address length
-	 * @return the socket
-	 * @throw net::Error on errors
-	 * @note Wrapper of accept(2)
-	 */
-	template <typename Address, typename Protocol>
-	Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length)
-	{
-		Handle handle = ::accept(sc.handle(), address, length);
-
-		if (handle == Invalid) {
-			throw Error{Error::System, "accept"};
-		}
-
-		return Socket<Address, Protocol>{handle, State::Accepted};
-	}
-
-	/**
-	 * Continue accept.
-	 *
-	 * This function is just present for compatibility, it should never be called.
-	 */
-	template <typename Address, typename Protocol>
-	inline void accept(Socket<Address, Protocol> &) const noexcept
-	{
-		/* no-op */
-	}
-
-	/**
-	 * Receive data.
-	 *
-	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
-	 * Condition::Readable. If 0 is returned and condition is not set, then the state is set to
-	 * State::Disconnected.
-	 *
-	 * If the socket is blocking, this function blocks until some data is available or if an error occurs.
-	 *
-	 * In any case, action is never set.
-	 *
-	 * @param sc the socket
-	 * @param data the destination
-	 * @param length the destination length
-	 * @return the number of bytes read
-	 * @throw Error on errors
-	 * @note Wrapper of recv(2)
-	 */
-	template <typename Address>
-	unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length)
-	{
-		int nbread = ::recv(sc.handle(), (Arg)data, length, 0);
-
-		if (nbread == Failure) {
-#if defined(_WIN32)
-			int error = WSAGetLastError();
-
-			if (error == WSAEWOULDBLOCK) {
-				nbread = 0;
-				sc.setCondition(Condition::Readable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "recv", error};
-			}
-#else
-			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				sc.setCondition(Condition::Readable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "recv"};
-			}
-#endif
-		} else if (nbread == 0) {
-			sc.setState(State::Disconnected);
-		}
-
-		return static_cast<unsigned>(nbread);
-	}
-
-	/**
-	 * Send some data.
-	 *
-	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
-	 * Condition::Writable.
-	 *
-	 * If the socket is blocking, this function blocks until the data has been sent.
-	 *
-	 * On any other errors, this function throw net::Error.
-	 *
-	 * @param sc the socket
-	 * @param data the buffer to send
-	 * @param length the buffer length
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 * @note Wrapper of send(2)
-	 */
-	template <typename Address>
-	unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length)
-	{
-		int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0);
-
-		if (nbsent == Failure) {
-#if defined(_WIN32)
-			int error = WSAGetLastError();
-
-			if (error == WSAEWOULDBLOCK) {
-				nbsent = 0;
-				sc.setCondition(Condition::Writable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "send", error};
-			}
-#else
-			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				nbsent = 0;
-				sc.setCondition(Condition::Writable);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "send"};
-			}
-#endif
-		}
-
-		return static_cast<unsigned>(nbsent);
-	}
-};
-
-/* }}} */
-
-/* {{{ Udp */
-
-/**
- * @class Udp
- * @brief Clear UDP type.
- *
- * This class is the basic implementation of UDP sockets.
- */
-class Udp {
-public:
-	/**
-	 * Socket type.
-	 *
-	 * @return SOCK_DGRAM
-	 */
-	inline int type() const noexcept
-	{
-		return SOCK_DGRAM;
-	}
-
-	/**
-	 * Do nothing.
-	 */
-	template <typename Address>
-	inline void create(Socket<Address, Udp> &) noexcept
-	{
-		/* No-op */
-	}
-
-	/**
-	 * Receive data from an end point.
-	 *
-	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
-	 * Condition::Readable.
-	 *
-	 * If the socket is blocking, this functions blocks until some data is available or if an error occurs.
-	 *
-	 * @param sc the socket
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param address the address
-	 * @param addrlen the initial address length
-	 * @return the number of bytes received
-	 * @throw Error on error
-	 */
-	template <typename Address>
-	unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
-	{
-		int nbread;
-
-		nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen);
-
-		if (nbread == Failure) {
-#if defined(_WIN32)
-			int error = WSAGetLastError();
-
-			if (error == WSAEWOULDBLOCK) {
-				nbread = 0;
-				sc.setCondition(Condition::Readable);
-			} else {
-				throw Error{Error::System, "recvfrom"};
-			}
-#else
-			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				nbread = 0;
-				sc.setCondition(Condition::Readable);
-			} else {
-				throw Error{Error::System, "recvfrom"};
-			}
-#endif
-		}
-
-		return static_cast<unsigned>(nbread);
-	}
-
-	/**
-	 * Send data to an end point.
-	 *
-	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
-	 * Condition::Writable.
-	 *
-	 * If the socket is blocking, this functions blocks until the data has been sent.
-	 *
-	 * @param sc the socket
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the client address
-	 * @param addrlen the adderss length
-	 * @return the number of bytes sent
-	 * @throw Error on error
-	 */
-	template <typename Address>
-	unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
-	{
-		int nbsent;
-
-		nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen);
-		if (nbsent == Failure) {
-#if defined(_WIN32)
-			int error = WSAGetLastError();
-
-			if (error == WSAEWOULDBLOCK) {
-				nbsent = 0;
-				sc.setCondition(Condition::Writable);
-			} else {
-				throw Error{Error::System, "sendto", error};
-			}
-#else
-			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				nbsent = 0;
-				sc.setCondition(Condition::Writable);
-			} else {
-				throw Error{Error::System, "sendto"};
-			}
-#endif
-		}
-
-		return static_cast<unsigned>(nbsent);
-	}
-};
-
-/* }}} */
-
-/* {{{ Tls */
-
-#if !defined(SOCKET_NO_SSL)
-
-/**
- * @class Tls
- * @brief OpenSSL secure layer for TCP.
- *
- * **Note:** This protocol is much more difficult to use with non-blocking sockets, if some operations would block, the
- * user is responsible of calling the function again by waiting for the appropriate condition. See the functions for
- * more details.
- *
- * @see Tls::accept
- * @see Tls::connect
- * @see Tls::recv
- * @see Tls::send
- */
-class Tls : private Tcp {
-private:
-	using Context = std::shared_ptr<SSL_CTX>;
-	using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>;
-
-	/* OpenSSL objects */
-	Context m_context;
-	Ssl m_ssl{nullptr, nullptr};
-
-	/* Status */
-	bool m_tcpconnected{false};
-
-	/*
-	 * User definable parameters
-	 */
-	ssl::Method m_method{ssl::Tlsv1};
-	std::string m_key;
-	std::string m_certificate;
-	bool m_verify{false};
-
-	/*
-	 * Construct with a context and ssl, for Tls::accept.
-	 */
-	Tls(Context context, Ssl ssl)
-		: m_context{std::move(context)}
-		, m_ssl{std::move(ssl)}
-	{
-	}
-
-	/*
-	 * Get the OpenSSL error message.
-	 */
-	inline std::string error(int error)
-	{
-		auto msg = ERR_reason_error_string(error);
-
-		return msg == nullptr ? "" : msg;
-	}
-
-	/*
-	 * Update the states after an uncompleted operation.
-	 */
-	template <typename Address, typename Protocol>
-	inline void updateStates(Socket<Address, Protocol> &sc, State state, Action action, int code)
-	{
-		assert(code == SSL_ERROR_WANT_READ || code == SSL_ERROR_WANT_WRITE);
-
-		sc.setState(state);
-		sc.setAction(action);
-
-		if (code == SSL_ERROR_WANT_READ) {
-			sc.setCondition(Condition::Readable);
-		} else {
-			sc.setCondition(Condition::Writable);
-		}
-	}
-
-	/*
-	 * Continue the connect operation.
-	 */
-	template <typename Address, typename Protocol>
-	void processConnect(Socket<Address, Protocol> &sc)
-	{
-		int ret = SSL_connect(m_ssl.get());
-
-		if (ret <= 0) {
-			int no = SSL_get_error(m_ssl.get(), ret);
-
-			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
-				updateStates(sc, State::Connecting, Action::Connect, no);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "connect", error(no)};
-			}
-		} else {
-			sc.setState(State::Connected);
-		}
-	}
-
-	/*
-	 * Continue accept.
-	 */
-	template <typename Address, typename Protocol>
-	void processAccept(Socket<Address, Protocol> &sc)
-	{
-		int ret = SSL_accept(m_ssl.get());
-
-		if (ret <= 0) {
-			int no = SSL_get_error(m_ssl.get(), ret);
-
-			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
-				updateStates(sc, State::Accepting, Action::Accept, no);
-			} else {
-				sc.setState(State::Disconnected);
-				throw Error(Error::System, "accept", error(no));
-			}
-		} else {
-			sc.setState(State::Accepted);
-		}
-	}
-
-public:
-	/**
-	 * @copydoc Tcp::type
-	 */
-	inline int type() const noexcept
-	{
-		return SOCK_STREAM;
-	}
-
-	/**
-	 * Empty TLS constructor.
-	 */
-	Tls()
-	{
-#if !defined(SOCKET_NO_SSL_AUTO_INIT)
-		net::ssl::init();
-#endif
-	}
-
-	/**
-	 * Set the method.
-	 *
-	 * @param method the method
-	 * @pre the socket must not be already created
-	 */
-	inline void setMethod(ssl::Method method) noexcept
-	{
-		assert(!m_context);
-		assert(!m_ssl);
-
-		m_method = method;
-	}
-
-	/**
-	 * Use the specified private key file.
-	 *
-	 * @param file the path to the private key
-	 */
-	inline void setPrivateKey(std::string file) noexcept
-	{
-		m_key = std::move(file);
-	}
-
-	/**
-	 * Use the specified certificate file.
-	 *
-	 * @param file the path to the file
-	 */
-	inline void setCertificate(std::string file) noexcept
-	{
-		m_certificate = std::move(file);
-	}
-
-	/**
-	 * Set to true if we must verify the certificate and private key.
-	 *
-	 * @param verify the mode
-	 */
-	inline void setVerify(bool verify = true) noexcept
-	{
-		m_verify = verify;
-	}
-
-	/**
-	 * Initialize the SSL objects after have created.
-	 *
-	 * @param sc the socket
-	 * @throw net::Error on errors
-	 */
-	template <typename Address>
-	inline void create(Socket<Address, Tls> &sc)
-	{
-		auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv23_method();
-
-		m_context = {SSL_CTX_new(method), SSL_CTX_free};
-		m_ssl = {SSL_new(m_context.get()), SSL_free};
-
-		SSL_set_fd(m_ssl.get(), sc.handle());
-
-		/* Load certificates */
-		if (m_certificate.size() > 0) {
-			SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM);
-		}
-		if (m_key.size() > 0) {
-			SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM);
-		}
-		if (m_verify && !SSL_CTX_check_private_key(m_context.get())) {
-			throw Error{Error::System, "(openssl)", "unable to verify key"};
-		}
-	}
-
-	/**
-	 * Connect to a secure host.
-	 *
-	 * If the socket is marked non-blocking and the connection cannot be established yet, then the state is set
-	 * to State::Connecting, the condition is set to Condition::Readable or Condition::Writable, the user must
-	 * wait for the appropriate condition before calling the overload connect which takes 0 argument.
-	 *
-	 * If the socket is blocking, this functions blocks until the connection is complete.
-	 *
-	 * If the connection was completed correctly the state is set to State::Connected.
-	 *
-	 * @param sc the socket
-	 * @param address the address
-	 * @param length the address length
-	 * @throw net::Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length)
-	{
-		/* 1. Connect using raw TCP */
-		Tcp::connect(sc, address, length);
-
-		/* 2. If the connection is complete (e.g. non-blocking), try handshake */
-		if (sc.state() == State::Connected) {
-			m_tcpconnected = true;
-			processConnect(sc);
-		}
-	}
-
-	/**
-	 * Continue the connection.
-	 *
-	 * This function must be called when the socket is ready for reading or writing (check with Socket::condition),
-	 * the state may change exactly like the initial connect call.
-	 *
-	 * @param sc the socket
-	 * @throw net::Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc)
-	{
-		/* 1. Be sure to complete standard connect before */
-		if (!m_tcpconnected) {
-			Tcp::connect(sc);
-			m_tcpconnected = sc.state() == State::Connected;
-		}
-
-		if (m_tcpconnected) {
-			processConnect(sc);
-		}
-	}
-
-	/**
-	 * Accept a secure client.
-	 *
-	 * Because SSL needs several round-trips, if the socket is marked non-blocking and the connection is not
-	 * completed yet, a new socket is returned but with the State::Accepting state. Its condition is set to
-	 * Condition::Readable or Condition::Writable, the user is responsible of calling accept overload which takes
-	 * 0 arguments on the returned socket when the condition is met.
-	 *
-	 * If the socket is blocking, this function blocks until the client is accepted and returned.
-	 *
-	 * If the client is accepted correctly, its state is set to State::Accepted. This instance does not change.
-	 *
-	 * @param sc the socket
-	 * @param address the address destination
-	 * @param length the address length
-	 * @return the client
-	 * @throw net::Error on errors
-	 */
-	template <typename Address>
-	Socket<Address, Tls> accept(Socket<Address, Tls> &sc, sockaddr *address, socklen_t *length)
-	{
-		Socket<Address, Tls> client = Tcp::accept(sc, address, length);
-		Tls &proto = client.protocol();
-
-		/* 1. Share the context */
-		proto.m_context = m_context;
-
-		/* 2. Create new SSL instance */
-		proto.m_ssl = Ssl{SSL_new(m_context.get()), SSL_free};
-		SSL_set_fd(proto.m_ssl.get(), client.handle());
-
-		/* 3. Try accept process on the **new** client */
-		proto.processAccept(client);
-
-		return client;
-	}
-
-	/**
-	 * Continue accept.
-	 *
-	 * This function must be called on the client that is being accepted.
-	 *
-	 * Like accept or connect, user is responsible of calling this function until the connection is complete.
-	 *
-	 * @param sc the socket
-	 * @throw net::Error on errors
-	 */
-	template <typename Address, typename Protocol>
-	inline void accept(Socket<Address, Protocol> &sc)
-	{
-		processAccept(sc);
-	}
-
-	/**
-	 * Receive some secure data.
-	 *
-	 * If the socket is marked non-blocking, 0 is returned if no data is available yet or if the connection
-	 * needs renegociation. If renegociation is required case, the action is set to Action::Receive and condition
-	 * is set to Condition::Readable or Condition::Writable. The user must wait that the condition is met and
-	 * call this function again.
-	 *
-	 * @param sc the socket
-	 * @param data the destination
-	 * @param len the buffer length
-	 * @return the number of bytes read
-	 * @throw net::Error on errors
-	 */
-	template <typename Address>
-	unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len)
-	{
-		auto nbread = SSL_read(m_ssl.get(), data, len);
-
-		if (nbread <= 0) {
-			auto no = SSL_get_error(m_ssl.get(), nbread);
-
-			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
-				nbread = 0;
-				updateStates(sc, sc.state(), Action::Receive, no);
-			} else {
-				throw Error{Error::System, "recv", error(no)};
-			}
-		}
-
-		return nbread;
-	}
-
-	/**
-	 * Send some data.
-	 *
-	 * Like recv, if the socket is marked non-blocking and no data can be sent or a negociation is required,
-	 * condition and action are set. See receive for more details
-	 *
-	 * @param sc the socket
-	 * @param data the data to send
-	 * @param len the buffer length
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 */
-	template <typename Address>
-	unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len)
-	{
-		auto nbsent = SSL_write(m_ssl.get(), data, len);
-
-		if (nbsent <= 0) {
-			auto no = SSL_get_error(m_ssl.get(), nbsent);
-
-			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
-				nbsent = 0;
-				updateStates(sc, sc.state(), Action::Send, no);
-			} else {
-				throw Error{Error::System, "send", error(no)};
-			}
-		}
-
-		return nbsent;
-	}
-};
-
-#endif // !SOCKET_NO_SSL
-
-/* }}} */
-
-} // !protocol
-
-/* }}} */
-
-/*
- * Convenient helpers
- * ------------------------------------------------------------------
- *
- * - SocketTcp<Address>, for TCP sockets,
- * - SocketUdp<Address>, for UDP sockets,
- * - SocketTls<Address>, for secure TCP sockets.
- */
-
-/* {{{ Helpers */
-
-/**
- * Helper to create TCP sockets.
- */
-template <typename Address>
-using SocketTcp = Socket<Address, protocol::Tcp>;
-
-/**
- * Helper to create TCP/IP sockets.
- */
-using SocketTcpIp = Socket<address::Ip, protocol::Tcp>;
-
-#if !defined(_WIN32)
-
-/**
- * Helper to create TCP/Local sockets.
- */
-using SocketTcpLocal = Socket<address::Local, protocol::Tcp>;
-
-#endif
-
-/**
- * Helper to create UDP sockets.
- */
-template <typename Address>
-using SocketUdp = Socket<Address, protocol::Udp>;
-
-/**
- * Helper to create UDP/IP sockets.
- */
-using SocketUdpIp = Socket<address::Ip, protocol::Udp>;
-
-#if !defined(SOCKET_NO_SSL)
-
-/**
- * Helper to create OpenSSL TCP sockets.
- */
-template <typename Address>
-using SocketTls = Socket<Address, protocol::Tls>;
-
-/**
- * Helper to create OpenSSL TCP/Ip sockets.
- */
-using SocketTlsIp = Socket<address::Ip, protocol::Tls>;
-
-#endif // !SOCKET_NO_SSL
-
-/* }}} */
-
-/*
- * Select wrapper
- * ------------------------------------------------------------------
- *
- * Wrapper for select(2) and other various implementations.
- */
-
-/* {{{ Listener */
-
-/**
- * @class ListenerStatus
- * @brief Result of polling
- *
- * Result of a select call, returns the first ready socket found with its
- * flags.
- */
-class ListenerStatus {
-public:
-	Handle socket;		//!< which socket is ready
-	Condition flags;	//!< the flags
-};
-
-/**
- * Table used in the socket listener to store which sockets have been
- * set in which directions.
- */
-using ListenerTable = std::map<Handle, Condition>;
-
-/**
- * @class Select
- * @brief Implements select(2)
- *
- * This class is the fallback of any other method, it is not preferred at all for many reasons.
- */
-class Select {
-public:
-	/**
-	 * No-op, uses the ListenerTable directly.
-	 */
-	inline void set(const ListenerTable &, Handle, Condition, bool) noexcept {}
-
-	/**
-	 * No-op, uses the ListenerTable directly.
-	 */
-	inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept {}
-
-	/**
-	 * Return the sockets
-	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &table, int ms);
-
-	/**
-	 * Backend identifier
-	 */
-	inline const char *name() const noexcept
-	{
-		return "select";
-	}
-};
-
-#if defined(SOCKET_HAVE_POLL)
-
-/**
- * @class Poll
- * @brief Implements poll(2).
- *
- * Poll is widely supported and is better than select(2). It is still not the
- * best option as selecting the sockets is O(n).
- */
-class Poll {
-private:
-	std::vector<pollfd> m_fds;
-
-	short toPoll(Condition flags) const noexcept;
-	Condition toCondition(short &event) const noexcept;
-
-public:
-	/**
-	 * Set the handle.
-	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Unset the handle.
-	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Wait for events.
-	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
-
-	/**
-	 * Backend identifier
-	 */
-	inline const char *name() const noexcept
-	{
-		return "poll";
-	}
-};
-
-#endif
-
-#if defined(SOCKET_HAVE_EPOLL)
-
-/**
- * @class Epoll
- * @brief Linux's epoll.
- */
-class Epoll {
-private:
-	int m_handle;
-	std::vector<epoll_event> m_events;
-
-	Epoll(const Epoll &) = delete;
-	Epoll &operator=(const Epoll &) = delete;
-	Epoll(const Epoll &&) = delete;
-	Epoll &operator=(const Epoll &&) = delete;
-
-	uint32_t toEpoll(Condition flags) const noexcept;
-	Condition toCondition(uint32_t events) const noexcept;
-	void update(Handle sc, int op, int eflags);
-
-public:
-	/**
-	 * Construct the epoll instance.
-	 */
-	Epoll();
-
-	/**
-	 * Close the epoll instance.
-	 */
-	~Epoll();
-
-	/**
-	 * Set the handle.
-	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Unset the handle.
-	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Wait for events.
-	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int);
-
-	/**
-	 * Backend identifier
-	 */
-	inline const char *name() const noexcept
-	{
-		return "epoll";
-	}
-};
-
-#endif
-
-#if defined(SOCKET_HAVE_KQUEUE)
-
-/**
- * @class Kqueue
- * @brief Implements kqueue(2).
- *
- * This implementation is available on all BSD and Mac OS X. It is better than
- * poll(2) because it's O(1), however it's a bit more memory consuming.
- */
-class Kqueue {
-private:
-	std::vector<struct kevent> m_result;
-	int m_handle;
-
-	Kqueue(const Kqueue &) = delete;
-	Kqueue &operator=(const Kqueue &) = delete;
-	Kqueue(Kqueue &&) = delete;
-	Kqueue &operator=(Kqueue &&) = delete;
-
-	void update(Handle sc, int filter, int kflags);
-
-public:
-	/**
-	 * Construct the kqueue instance.
-	 */
-	Kqueue();
-
-	/**
-	 * Destroy the kqueue instance.
-	 */
-	~Kqueue();
-
-	/**
-	 * Set the handle.
-	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Unset the handle.
-	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
-
-	/**
-	 * Wait for events.
-	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int);
-
-	/**
-	 * Backend identifier
-	 */
-	inline const char *name() const noexcept
-	{
-		return "kqueue";
-	}
-};
-
-#endif
-
-/**
- * @class Listener
- * @brief Synchronous multiplexing
- *
- * Convenient wrapper around the select() system call.
- *
- * This class is implemented using a bridge pattern to allow different uses
- * of listener implementation.
- *
- * You should not reinstanciate a new Listener at each iteartion of your
- * main loop as it can be extremely costly. Instead use the same listener that
- * you can safely modify on the fly.
- *
- * Currently, poll, epoll, select and kqueue are available.
- *
- * To implement the backend, the following functions must be available:
- *
- * ### Set
- *
- * @code
- * void set(const ListenerTable &, Handle sc, Condition condition, bool add);
- * @endcode
- *
- * This function, takes the socket to be added and the flags. The condition is
- * always guaranteed to be correct and the function will never be called twice
- * even if the user tries to set the same flag again.
- *
- * An optional add argument is added for backends which needs to do different
- * operation depending if the socket was already set before or if it is the
- * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7).
- *
- * ### Unset
- *
- * @code
- * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove);
- * @endcode
- *
- * Like set, this function is only called if the condition is actually set and will
- * not be called multiple times.
- *
- * Also like set, an optional remove argument is set if the socket is being
- * completely removed (e.g no more flags are set for this socket).
- *
- * ### Wait
- *
- * @code
- * std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
- * @endcode
- *
- * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus,
- * may throw any exceptions.
- *
- * ### Name
- *
- * @code
- * inline const char *name() const noexcept
- * @endcode
- *
- * Returns the backend name. Usually the class in lower case.
- */
-template <typename Backend = SOCKET_DEFAULT_BACKEND>
-class Listener {
-private:
-	Backend m_backend;
-	ListenerTable m_table;
-
-public:
-	/**
-	 * Construct an empty listener.
-	 */
-	Listener() = default;
-
-	/**
-	 * Get the backend.
-	 *
-	 * @return the backend
-	 */
-	inline const Backend &backend() const noexcept
-	{
-		return m_backend;
-	}
-
-	/**
-	 * Get the non-modifiable table.
-	 *
-	 * @return the table
-	 */
-	inline const ListenerTable &table() const noexcept
-	{
-		return m_table;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline ListenerTable::const_iterator begin() const noexcept
-	{
-		return m_table.begin();
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline ListenerTable::const_iterator cbegin() const noexcept
-	{
-		return m_table.cbegin();
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline ListenerTable::const_iterator end() const noexcept
-	{
-		return m_table.end();
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline ListenerTable::const_iterator cend() const noexcept
-	{
-		return m_table.cend();
-	}
-
-	/**
-	 * Add or update a socket to the listener.
-	 *
-	 * If the socket is already placed with the appropriate flags, the
-	 * function is a no-op.
-	 *
-	 * If incorrect flags are passed, the function does nothing.
-	 *
-	 * @param sc the socket
-	 * @param condition the condition (may be OR'ed)
-	 * @throw Error if the backend failed to set
-	 */
-	void set(Handle sc, Condition condition)
-	{
-		/* Invalid or useless flags */
-		if (condition == Condition::None || static_cast<int>(condition) > 0x3)
-			return;
-
-		auto it = m_table.find(sc);
-
-		/*
-		 * Do not update the table if the backend failed to add
-		 * or update.
-		 */
-		if (it == m_table.end()) {
-			m_backend.set(m_table, sc, condition, true);
-			m_table.emplace(sc, condition);
-		} else {
-			/* Remove flag if already present */
-			if ((condition & Condition::Readable) == Condition::Readable &&
-			    (it->second & Condition::Readable) == Condition::Readable) {
-				condition &= ~(Condition::Readable);
-			}
-			if ((condition & Condition::Writable) == Condition::Writable &&
-			    (it->second & Condition::Writable) == Condition::Writable) {
-				condition &= ~(Condition::Writable);
-			}
-
-			/* Still need a call? */
-			if (condition != Condition::None) {
-				m_backend.set(m_table, sc, condition, false);
-				it->second |= condition;
-			}
-		}
-	}
-
-	/**
-	 * Unset a socket from the listener, only the flags is removed
-	 * unless the two flagss are requested.
-	 *
-	 * For example, if you added a socket for both reading and writing,
-	 * unsetting the write flags will keep the socket for reading.
-	 *
-	 * @param sc the socket
-	 * @param condition the condition (may be OR'ed)
-	 * @see remove
-	 */
-	void unset(Handle sc, Condition condition)
-	{
-		auto it = m_table.find(sc);
-
-		/* Invalid or useless flags */
-		if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end())
-			return;
-
-		/*
-		 * Like set, do not update if the socket is already at the appropriate
-		 * state.
-		 */
-		if ((condition & Condition::Readable) == Condition::Readable &&
-		    (it->second & Condition::Readable) != Condition::Readable) {
-			condition &= ~(Condition::Readable);
-		}
-		if ((condition & Condition::Writable) == Condition::Writable &&
-		    (it->second & Condition::Writable) != Condition::Writable) {
-			condition &= ~(Condition::Writable);
-		}
-
-		if (condition != Condition::None) {
-			/* Determine if it's a complete removal */
-			bool removal = ((it->second) & ~(condition)) == Condition::None;
-
-			m_backend.unset(m_table, sc, condition, removal);
-
-			if (removal) {
-				m_table.erase(it);
-			} else {
-				it->second &= ~(condition);
-			}
-		}
-	}
-
-	/**
-	 * Remove completely the socket from the listener.
-	 *
-	 * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable);
-	 *
-	 * @param sc the socket
-	 */
-	inline void remove(Handle sc)
-	{
-		unset(sc, Condition::Readable | Condition::Writable);
-	}
-
-	/**
-	 * Remove all sockets.
-	 */
-	inline void clear()
-	{
-		while (!m_table.empty()) {
-			remove(m_table.begin()->first);
-		}
-	}
-
-	/**
-	 * Get the number of sockets in the listener.
-	 */
-	inline ListenerTable::size_type size() const noexcept
-	{
-		return m_table.size();
-	}
-
-	/**
-	 * Select a socket. Waits for a specific amount of time specified as the duration.
-	 *
-	 * @param duration the duration
-	 * @return the socket ready
-	 */
-	template <typename Rep, typename Ratio>
-	inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration)
-	{
-		assert(!m_table.empty());
-
-		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
-
-		return m_backend.wait(m_table, cvt.count())[0];
-	}
-
-	/**
-	 * Overload with milliseconds.
-	 *
-	 * @param timeout the optional timeout in milliseconds
-	 * @return the socket ready
-	 */
-	inline ListenerStatus wait(int timeout = -1)
-	{
-		return wait(std::chrono::milliseconds(timeout));
-	}
-
-	/**
-	 * Select multiple sockets.
-	 *
-	 * @param duration the duration
-	 * @return the socket ready
-	 */
-	template <typename Rep, typename Ratio>
-	inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration)
-	{
-		assert(!m_table.empty());
-
-		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
-
-		return m_backend.wait(m_table, cvt.count());
-	}
-
-	/**
-	 * Overload with milliseconds.
-	 *
-	 * @return the socket ready
-	 */
-	inline std::vector<ListenerStatus> waitMultiple(int timeout = -1)
-	{
-		return waitMultiple(std::chrono::milliseconds(timeout));
-	}
-};
-
-/* }}} */
-
-/*
- * Callback
- * ------------------------------------------------------------------
- *
- * Function owner with tests.
- */
-
-/* {{{ Callback */
-
-/**
- * @class Callback
- * @brief Convenient signal owner that checks if the target is valid.
- *
- * This class also catch all errors thrown from signals to avoid interfering with our process.
- */
-template <typename... Args>
-class Callback : public std::function<void (Args...)> {
-public:
-	/**
-	 * Inherited constructors.
-	 */
-	using std::function<void (Args...)>::function;
-
-	/**
-	 * Execute the callback only if a target is set.
-	 */
-	void operator()(Args... args) const
-	{
-		if (*this) {
-			try {
-				std::function<void (Args...)>::operator()(args...);
-			} catch (...) {
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamConnection
- * ------------------------------------------------------------------
- *
- * Client connected on the server side.
- */
-
-/* {{{ StreamConnection */
-
-/**
- * @class StreamConnection
- * @brief Connected client on the server side.
- *
- * This object is created from StreamServer when a new client is connected, it is the higher
- * level object of sockets and completely asynchronous.
- */
-template <typename Address, typename Protocol>
-class StreamConnection {
-public:
-	/**
-	 * Called when the output has changed.
-	 */
-	using WriteHandler = Callback<>;
-
-private:
-	/* Signals */
-	WriteHandler m_onWrite;
-
-	/* Sockets and output buffer */
-	Socket<Address, Protocol> m_socket;
-	std::string m_output;
-
-public:
-	/**
-	 * Create the connection.
-	 *
-	 * @param s the socket
-	 */
-	StreamConnection(Socket<Address, Protocol> s)
-		: m_socket{std::move(s)}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-	}
-
-	/**
-	 * Access the underlying socket.
-	 *
-	 * @return the socket
-	 * @warning use with care
-	 */
-	inline Socket<Address, Protocol> &socket() noexcept
-	{
-		return m_socket;
-	}
-
-	/**
-	 * Access the current output.
-	 *
-	 * @return the output
-	 */
-	inline const std::string &output() const noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Overloaded function
-	 *
-	 * @return the output
-	 * @warning use with care, avoid modifying the output if you don't know what you're doing
-	 */
-	inline std::string &output() noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Post some data to be sent asynchronously.
-	 *
-	 * @param str the data to append
-	 */
-	inline void send(std::string str)
-	{
-		m_output += str;
-		m_onWrite();
-	}
-
-	/**
-	 * Kill the client.
-	 */
-	inline void close()
-	{
-		m_socket.close();
-	}
-
-	/**
-	 * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner
-	 * knows that there are some data to send.
-	 *
-	 * @param handler the handler
-	 * @warning you usually never need to set this yourself
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-};
-
-/* }}} */
-
-/*
- * StreamServer
- * ------------------------------------------------------------------
- *
- * Convenient stream oriented server.
- */
-
-/* {{{ StreamServer */
-
-/**
- * @class StreamServer
- * @brief Convenient stream server for TCP and TLS.
- *
- * This class does all the things for you as accepting new clients, listening for it and sending data. It works
- * asynchronously without blocking to let you control your process workflow.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamServer {
-public:
-	/**
-	 * Handler when a new client is connected.
-	 */
-	using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when a client is disconnected.
-	 */
-	using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when data has been received from a client.
-	 */
-	using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when data has been correctly sent to a client.
-	 */
-	using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when an error occured.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when there was a timeout.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>;
-
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	DisconnectionHandler m_onDisconnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Sockets */
-	Socket<Address, Protocol> m_master;
-	Listener<> m_listener;
-	ClientMap m_clients;
-
-	/*
-	 * Update flags depending on the required condition.
-	 */
-	void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		assert(client->socket().action() != Action::None);
-
-		m_listener.remove(client->socket().handle());
-		m_listener.set(client->socket().handle(), client->socket().condition());
-	}
-
-	/*
-	 * Continue accept process.
-	 */
-	template <typename AcceptCall>
-	void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc)
-	{
-		try {
-			/* Do the accept */
-			acceptFunc();
-
-			/* 1. First remove completely the client */
-			m_listener.remove(client->socket().handle());
-
-			/* 2. If accept is not finished, wait for the appropriate condition */
-			if (client->socket().state() == State::Accepted) {
-				/* 3. Client is accepted, notify the user */
-				m_listener.set(client->socket().handle(), Condition::Readable);
-				m_onConnection(client);
-			} else {
-				/* Operation still in progress */
-				updateFlags(client);
-			}
-		} catch (const Error &error) {
-			m_clients.erase(client->socket().handle());
-			m_listener.remove(client->socket().handle());
-			m_onError(error);
-		}
-	}
-
-	/*
-	 * Process initial accept of master socket, this is the initial accepting process. Except on errors, the
-	 * socket is stored but the user will be notified only once the socket is completely accepted.
-	 */
-	void processInitialAccept()
-	{
-		// TODO: store address too.
-		std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr));
-		std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client};
-
-		/* 1. Register output changed to update listener */
-		client->setWriteHandler([this, ptr] () {
-			auto client = ptr.lock();
-
-			/* Do not update the listener immediately if an action is pending */
-			if (client && client->socket().action() == Action::None && !client->output().empty()) {
-				m_listener.set(client->socket().handle(), Condition::Writable);
-			}
-		});
-
-		/* 2. Add the client */
-		m_clients.insert(std::make_pair(client->socket().handle(), client));
-
-		/*
-		 * 2. Do an initial check to set the listener flags, at this moment the socket may or not be
-		 *    completely accepted.
-		 */
-		processAccept(client, [&] () {});
-	}
-
-	/*
-	 * Read or complete the read operation.
-	 */
-	void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		/*
-		 * Read because there is something to read or because the pending operation is
-		 * read and must complete.
-		 */
-		auto buffer = client->socket().recv(512);
-
-		/*
-		 * Now the receive operation may be completed, in that case, two possibilities:
-		 *
-		 * 1. The action is set to None (completed)
-		 * 2. The action is still not complete, update the flags
-		 */
-		if (client->socket().action() == Action::None) {
-			/* Empty mean normal disconnection */
-			if (buffer.empty()) {
-				m_listener.remove(client->socket().handle());
-				m_clients.erase(client->socket().handle());
-				m_onDisconnection(client);
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (!client->output().empty()) {
-					m_listener.set(client->socket().handle(), Condition::Writable);
-				}
-
-				m_onRead(client, buffer);
-			}
-		} else {
-			/* Operation in progress */
-			updateFlags(client);
-		}
-	}
-
-	/*
-	 * Flush the output buffer.
-	 */
-	void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		auto &output = client->output();
-		auto nsent = client->socket().send(output);
-
-		if (client->socket().action() == Action::None) {
-			/* 1. Create a copy of content that has been sent */
-			auto sent = output.substr(0, nsent);
-
-			/* 2. Erase the content sent */
-			output.erase(0, nsent);
-
-			/* 3. Update listener */
-			if (output.empty()) {
-				m_listener.unset(client->socket().handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(client, sent);
-		} else {
-			updateFlags(client);
-		}
-	}
-
-	void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags)
-	{
-		try {
-			auto action = client->socket().action();
-
-			if (action == Action::Receive ||
-			    (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) {
-				processRead(client);
-			} else if ((flags & Condition::Writable) == Condition::Writable) {
-				processWrite(client);
-			}
-		} catch (const Error &error) {
-			m_onDisconnection(client);
-			m_listener.remove(client->socket().handle());
-			m_clients.erase(client->socket().handle());
-		}
-	}
-
-public:
-	/**
-	 * Create a stream server with the specified address to bind.
-	 *
-	 * @param protocol the protocol (Tcp or Tls)
-	 * @param address the address to bind
-	 * @param max the max number to listen
-	 * @throw Error on errors
-	 */
-	StreamServer(Protocol protocol, const Address &address, int max = 128)
-		: m_master{std::move(protocol), address}
-	{
-		// TODO: m_onError
-		m_master.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_master.bind(address);
-		m_master.listen(max);
-		m_listener.set(m_master.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when a new client is connected.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when a client died.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the receive handler, called when a client has sent something.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the writing handler, called when some data has been sent to a client.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unrecoverable error has occured.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Set the timeout handler, called when the selection has timeout.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setTimeoutHandler(TimeoutHandler handler)
-	{
-		m_onTimeout = std::move(handler);
-	}
-
-	/**
-	 * Poll for the next event.
-	 *
-	 * @param timeout the timeout (-1 for indefinitely)
-	 * @throw Error on errors
-	 */
-	void poll(int timeout = -1)
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (st.socket == m_master.handle()) {
-				/* New client */
-				processInitialAccept();
-			} else {
-				/* Recv / Send / Accept on a client */
-				auto client = m_clients[st.socket];
-
-				if (client->socket().state() == State::Accepted) {
-					processSync(client, st.flags);
-				} else {
-					processAccept(client, [&] () { client->socket().accept(); });
-				}
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_onError(error);
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamClient
- * ------------------------------------------------------------------
- */
-
-/* {{{ StreamClient */
-
-/**
- * @class StreamClient
- * @brief Client side connection to a server.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamClient {
-public:
-	/**
-	 * Handler when connection is complete.
-	 */
-	using ConnectionHandler = Callback<>;
-
-	/**
-	 * Handler when data has been received.
-	 */
-	using ReadHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when data has been sent correctly.
-	 */
-	using WriteHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when disconnected.
-	 */
-	using DisconnectionHandler = Callback<>;
-
-	/**
-	 * Handler on unrecoverable error.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when timeout occured.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	DisconnectionHandler m_onDisconnection;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Socket */
-	Socket<Address, Protocol> m_socket;
-	Listener<> m_listener;
-
-	/* Output buffer */
-	std::string m_output;
-
-	/*
-	 * Update the flags after an uncompleted operation. This function must only be called when the operation
-	 * has not complete (e.g. connect, recv, send).
-	 */
-	void updateFlags()
-	{
-		assert(m_socket.action() != Action::None);
-
-		m_listener.remove(m_socket.handle());
-		m_listener.set(m_socket.handle(), m_socket.condition());
-	}
-
-	/*
-	 * This is the generic connect helper, it will be used to both initiate the connection or to continue the
-	 * connection process if needed.
-	 *
-	 * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or
-	 * m_socket.connect().
-	 *
-	 * See poll() and connect() to understand.
-	 */
-	template <typename ConnectCall>
-	void processConnect(const ConnectCall &connectFunc)
-	{
-		/* Call m_socket.connect() or m_socket.connect(address) */
-		connectFunc();
-
-		/* Remove entirely */
-		m_listener.remove(m_socket.handle());
-
-		if (m_socket.state() == State::Connected) {
-			m_onConnection();
-			m_listener.set(m_socket.handle(), Condition::Readable);
-		} else {
-			/* Connection still in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or complete the receive command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processRead()
-	{
-		auto received = m_socket.recv(512);
-
-		if (m_socket.action() == Action::None) {
-			/* 0 means disconnection */
-			if (received.empty()) {
-				m_onDisconnection();
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (m_output.empty()) {
-					m_listener.unset(m_socket.handle(), Condition::Writable);
-				}
-
-				m_onRead(received);
-			}
-		} else {
-			/* Receive operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Send or complete the send command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processWrite()
-	{
-		auto nsent = m_socket.send(m_output);
-
-		if (m_socket.action() == Action::None) {
-			/* 1. Make a copy of what has been sent */
-			auto sent = m_output.substr(0, nsent);
-
-			/* 2. Erase sent content */
-			m_output.erase(0, nsent);
-
-			/* 3. Update flags if needed */
-			if (m_output.empty()) {
-				m_listener.unset(m_socket.handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(sent);
-		} else {
-			/* Send operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or send.
-	 */
-	void processSync(Condition condition)
-	{
-		if ((m_socket.action() == Action::Receive) ||
-		    (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) {
-			processRead();
-		} else {
-			processWrite();
-		}
-	}
-
-public:
-	/**
-	 * Create a client. The client is automatically marked as non-blocking.
-	 *
-	 * @param protocol the protocol (Tcp or Tls)
-	 * @param address the optional address
-	 * @throw net::Error on failures
-	 */
-	StreamClient(Protocol protocol = {}, const Address &address = {})
-		: m_socket{std::move(protocol), address}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-		m_listener.set(m_socket.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when the connection succeed.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when the server closed the connection.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the read handler, called when we received something.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the write handler, called when we successfully sent data.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unexpected error occurs.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Connect to a server, this function may connect immediately or not in any case the connection handler
-	 * will be called when the connection completed.
-	 *
-	 * @param address the address to connect to
-	 */
-	void connect(const Address &address) noexcept
-	{
-		assert(m_socket.state() == State::Open);
-
-		processConnect([&] () { m_socket.connect(address); });
-	}
-
-	/**
-	 * Asynchronously send data to the server.
-	 *
-	 * @param str the data to append
-	 */
-	void send(std::string str)
-	{
-		m_output += str;
-
-		/* Don't update the listener if there is a pending operation */
-		if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) {
-			m_listener.set(m_socket.handle(), Condition::Writable);
-		}
-	}
-
-	/**
-	 * Wait for the next event.
-	 *
-	 * @param timeout the time to wait in milliseconds
-	 * @throw Error on errors
-	 */
-	void poll(int timeout = -1) noexcept
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (m_socket.state() != State::Connected) {
-				/* Continue the connection */
-				processConnect([&] () { m_socket.connect(); });
-			} else {
-				/* Read / Write */
-				processSync(st.flags);
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_listener.remove(m_socket.handle());
-				m_onError(error);
-			}
-		}
-	}
-};
-
-/* }}} */
-
-} // !net
-
-} // !irccd
-
-#endif // !_SOCKETS_H_
--- a/lib/irccd/private/xdg.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * Xdg.cpp -- XDG directory specifications
- *
- * Copyright (c) 2013, 2014, 2015 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 <cstdlib>
-#include <stdexcept>
-#include <sstream>
-
-#include "xdg.h"
-
-namespace irccd {
-
-namespace {
-
-bool isabsolute(const std::string &path)
-{
-	return path.length() > 0 && path[0] == '/';
-}
-
-std::vector<std::string> split(const std::string &arg)
-{
-	std::stringstream iss(arg);
-	std::string item;
-	std::vector<std::string> elems;
-
-	while (std::getline(iss, item, ':'))
-		if (isabsolute(item))
-			elems.push_back(item);
-
-	return elems;
-}
-
-std::string envOrHome(const std::string &var, const std::string &repl)
-{
-	auto value = getenv(var.c_str());
-
-	if (value == nullptr || !isabsolute(value)) {
-		auto home = getenv("HOME");
-
-		if (home == nullptr)
-			throw std::runtime_error("could not get home directory");
-
-		return std::string(home) + "/" + repl;
-	}
-
-	return value;
-}
-
-std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list)
-{
-	auto value = getenv(var.c_str());
-
-	if (!value)
-		return list;
-
-	// No valid item at all? Use defaults
-	auto result = split(value);
-
-	return (result.size() == 0) ? list : result;
-}
-
-} // !namespace
-
-Xdg::Xdg()
-{
-	m_configHome	= envOrHome("XDG_CONFIG_HOME", ".config");
-	m_dataHome	= envOrHome("XDG_DATA_HOME", ".local/share");
-	m_cacheHome	= envOrHome("XDG_CACHE_HOME", ".cache");
-
-	m_configDirs	= listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" });
-	m_dataDirs	= listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" });
-
-	/*
-	 * Runtime directory is a special case and does not have a replacement, the
-	 * application should manage this by itself.
-	 */
-	auto runtime = getenv("XDG_RUNTIME_DIR");
-	if (runtime && isabsolute(runtime))
-		m_runtimeDir = runtime;
-}
-
-const std::string &Xdg::configHome() const noexcept
-{
-	return m_configHome;
-}
-
-const std::string &Xdg::dataHome() const noexcept
-{
-	return m_dataHome;
-}
-
-const std::string &Xdg::cacheHome() const noexcept
-{
-	return m_cacheHome;
-}
-
-const std::string &Xdg::runtimeDir() const
-{
-	if (m_runtimeDir.size() == 0)
-		throw std::runtime_error("XDG_RUNTIME_DIR is not set");
-
-	return m_runtimeDir;
-}
-
-const Xdg::List &Xdg::configDirs() const noexcept
-{
-	return m_configDirs;
-}
-
-const Xdg::List &Xdg::dataDirs() const noexcept
-{
-	return m_dataDirs;
-}
-
-} // !irccd
--- a/lib/irccd/private/xdg.h	Sat Mar 26 14:41:53 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Xdg.h -- XDG directory specifications
- *
- * Copyright (c) 2013, 2014, 2015 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_XDG_H_
-#define _IRCCD_XDG_H_
-
-/**
- * @file Xdg.h
- * @brief Read XDG standard specifications
- */
-
-#include <vector>
-#include <string>
-
-namespace irccd {
-
-/**
- * @class Xdg
- * @brief XDG specifications
- *
- * Read and get XDG directories. This file contains exports thingies so it can
- * compiles successfully on Windows but its usage is discouraged.
- */
-class Xdg {
-public:
-	/**
-	 * list of directories.
-	 */
-	using List	= std::vector<std::string>;
-
-private:
-	std::string	m_configHome;
-	std::string	m_dataHome;
-	std::string	m_cacheHome;
-	std::string	m_runtimeDir;
-	List		m_configDirs;
-	List		m_dataDirs;
-
-public:
-	/**
-	 * Open an xdg instance and load directories.
-	 *
-	 * @throw std::runtime_error on failures
-	 */
-	Xdg();
-
-	/**
-	 * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config
-	 *
-	 * @return the config directory
-	 */
-	const std::string &configHome() const noexcept;
-
-	/**
-	 * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share
-	 *
-	 * @return the data directory
-	 */
-	const std::string &dataHome() const noexcept;
-
-	/**
-	 * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache
-	 *
-	 * @return the cache directory
-	 */
-	const std::string &cacheHome() const noexcept;
-
-	/**
-	 * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set,
-	 * if not, it throws an exception.
-	 *
-	 * The XDG standard says that application should handle XDG_RUNTIME_DIR by
-	 * themselves.
-	 *
-	 * @return the runtime directory
-	 * @throw std::runtime_error on error
-	 */
-	const std::string &runtimeDir() const;
-
-	/**
-	 * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" }
-	 *
-	 * @return the list of config directories
-	 */
-	const List &configDirs() const noexcept;
-
-	/**
-	 * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" }
-	 *
-	 * @return the list of data directories
-	 */
-	const List &dataDirs() const noexcept;
-};
-
-} // !irccd
-
-#endif // !_IRCCD_XDG_H_
--- a/lib/irccd/server-state.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/server-state.h	Wed Mar 30 13:52:47 2016 +0200
@@ -21,8 +21,8 @@
 
 #include <irccd-config.h>
 
-#include <irccd/private/elapsed-timer.h>
-#include <irccd/private/sockets.h>
+#include "elapsed-timer.h"
+#include "sockets.h"
 
 namespace irccd {
 
--- a/lib/irccd/server.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/server.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -24,14 +24,9 @@
 #include <libirc_rfcnumeric.h>
 
 #include "logger.h"
+#include "server.h"
 #include "util.h"
 
-#include "server.h"
-
-#if defined(WITH_JS)
-#  include "js/js.h"
-#endif
-
 namespace irccd {
 
 bool Server::isSelf(const std::string &nick) const noexcept
@@ -436,13 +431,4 @@
 	irc_process_select_descriptors(m_session.get(), &setinput, &setoutput);
 }
 
-#if defined(WITH_JS)
-
-void Server::prototype(js::Context &ctx)
-{
-	ctx.getGlobal<void>("\xff""\xff""Server-proto");
-}
-
-#endif
-
 } // !irccd
--- a/lib/irccd/server.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/server.h	Wed Mar 30 13:52:47 2016 +0200
@@ -34,10 +34,9 @@
 
 #include <irccd-config.h>
 
-#include <irccd/private/signals.h>
-
 #include "logger.h"
 #include "server-state.h"
+#include "signals.h"
 
 namespace irccd {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/signals.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,175 @@
+/*
+ * signals.h -- synchronous observer mechanism
+ *
+ * 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_SIGNALS_H_
+#define _IRCCD_SIGNALS_H_
+
+#include <functional>
+#include <stack>
+#include <unordered_map>
+#include <vector>
+
+/**
+ * @file Signals.h
+ * @brief Similar Qt signal subsystem for irccd
+ */
+
+namespace irccd {
+
+/**
+ * @class SignalConnection
+ * @brief Stores the reference to the callable
+ *
+ * This class can be stored to remove a registered function from a Signal, be
+ * careful to not mix connections between different signals as they are just
+ * referenced by ids.
+ */
+class SignalConnection {
+private:
+	unsigned m_id;
+
+public:
+	/**
+	 * Create a signal connection.
+	 *
+	 * @param id the id
+	 */
+	inline SignalConnection(unsigned id) noexcept
+		: m_id(id)
+	{
+	}
+
+	/**
+	 * Get the reference object.
+	 *
+	 * @return the id
+	 */
+	inline unsigned id() const noexcept
+	{
+		return m_id;
+	}
+};
+
+/**
+ * @class Signal
+ * @brief Stores and call registered functions
+ *
+ * This class is intended to be use as a public field in the desired object.
+ *
+ * The user just have to call one of connect(), disconnect() or the call
+ * operator to use this class.
+ *
+ * It stores the callable as std::function so type-erasure is complete.
+ *
+ * The user is responsible of taking care that the object is still alive
+ * in case that the function takes a reference to the object.
+ */
+template <typename... Args>
+class Signal {
+private:
+	using Function = std::function<void (Args...)>;
+	using FunctionMap = std::unordered_map<unsigned, Function>;
+	using Stack = std::stack<unsigned>;
+
+	FunctionMap m_functions;
+	Stack m_stack;
+	unsigned m_max{0};
+
+public:
+	/**
+	 * Register a new function to the signal.
+	 *
+	 * @param function the function
+	 * @return the connection in case you want to remove it
+	 */
+	inline SignalConnection connect(Function function) noexcept
+	{
+		unsigned id;
+
+		if (!m_stack.empty()) {
+			id = m_stack.top();
+			m_stack.pop();
+		} else {
+			id = m_max ++;
+		}
+
+		m_functions.emplace(id, std::move(function));
+
+		return SignalConnection{id};
+	}
+
+	/**
+	 * Disconnect a connection.
+	 *
+	 * @param connection the connection
+	 * @warning Be sure that the connection belongs to that signal
+	 */
+	inline void disconnect(const SignalConnection &connection) noexcept
+	{
+		auto value = m_functions.find(connection.id());
+
+		if (value != m_functions.end()) {
+			m_functions.erase(connection.id());
+			m_stack.push(connection.id());
+		}
+	}
+
+	/**
+	 * Remove all registered functions.
+	 */
+	inline void clear()
+	{
+		m_functions.clear();
+		m_max = 0;
+
+		while (!m_stack.empty())
+			m_stack.pop();
+	}
+
+	/**
+	 * Call every functions.
+	 *
+	 * @param args the arguments to pass to the signal
+	 */
+	void operator()(Args... args) const
+	{
+		/*
+		 * Make a copy of the ids before iterating because the callbacks may eventually remove or modify
+		 * the list.
+		 */
+		std::vector<unsigned> ids;
+
+		for (auto &pair : m_functions)
+			ids.push_back(pair.first);
+
+		/*
+		 * Now iterate while checking if the next id is still available, however if any new signals were
+		 * added while iterating, they will not be called immediately.
+		 */
+		for (unsigned i : ids) {
+			auto it = m_functions.find(i);
+
+			if (it != m_functions.end())
+				it->second(args...);
+		}
+	}
+};
+
+} // !irccd
+
+#endif // !_IRCCD_SIGNALS_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/sockets.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,713 @@
+/*
+ * sockets.cpp -- portable C++ socket wrappers
+ *
+ * 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.
+ */
+
+#define TIMEOUT_MSG "operation timeout"
+
+#include <algorithm>
+#include <atomic>
+#include <cstring>
+#include <mutex>
+
+#include "sockets.h"
+
+namespace irccd {
+
+namespace net {
+
+/*
+ * Portable constants
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Constants */
+
+#if defined(_WIN32)
+
+const Handle Invalid{INVALID_SOCKET};
+const int Failure{SOCKET_ERROR};
+
+#else
+
+const Handle Invalid{-1};
+const int Failure{-1};
+
+#endif
+
+/* }}} */
+
+/*
+ * Portable functions
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Functions */
+
+#if defined(_WIN32)
+
+namespace {
+
+static std::mutex s_mutex;
+static std::atomic<bool> s_initialized{false};
+
+} // !namespace
+
+#endif // !_WIN32
+
+void init() noexcept
+{
+#if defined(_WIN32)
+	std::lock_guard<std::mutex> lock(s_mutex);
+
+	if (!s_initialized) {
+		s_initialized = true;
+
+		WSADATA wsa;
+		WSAStartup(MAKEWORD(2, 2), &wsa);
+
+		/*
+		 * If SOCKET_WSA_NO_INIT is not set then the user
+		 * must also call finish himself.
+		 */
+#if !defined(SOCKET_NO_AUTO_INIT)
+		atexit(finish);
+#endif
+	}
+#endif
+}
+
+void finish() noexcept
+{
+#if defined(_WIN32)
+	WSACleanup();
+#endif
+}
+
+std::string error(int errn)
+{
+#if defined(_WIN32)
+	LPSTR str = nullptr;
+	std::string errmsg = "Unknown error";
+
+	FormatMessageA(
+		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+		NULL,
+		errn,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPSTR)&str, 0, NULL);
+
+
+	if (str) {
+		errmsg = std::string(str);
+		LocalFree(str);
+	}
+
+	return errmsg;
+#else
+	return strerror(errn);
+#endif
+}
+
+std::string error()
+{
+#if defined(_WIN32)
+	return error(WSAGetLastError());
+#else
+	return error(errno);
+#endif
+}
+
+/* }}} */
+
+/*
+ * SSL stuff
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ SSL initialization */
+
+#if !defined(SOCKET_NO_SSL)
+
+namespace ssl {
+
+namespace {
+
+std::mutex mutex;
+std::atomic<bool> initialized{false};
+
+} // !namespace
+
+void finish() noexcept
+{
+	ERR_free_strings();
+}
+
+void init() noexcept
+{
+	std::lock_guard<std::mutex> lock{mutex};
+
+	if (!initialized) {
+		initialized = true;
+
+		SSL_library_init();
+		SSL_load_error_strings();
+		OpenSSL_add_all_algorithms();
+
+#if !defined(SOCKET_NO_AUTO_SSL_INIT)
+		atexit(finish);
+#endif // SOCKET_NO_AUTO_SSL_INIT
+	}
+}
+
+} // !ssl
+
+#endif // SOCKET_NO_SSL
+
+/* }}} */
+
+/*
+ * Error class
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Error */
+
+Error::Error(Code code, std::string function)
+	: m_code{code}
+	, m_function{std::move(function)}
+	, m_error{error()}
+{
+}
+
+Error::Error(Code code, std::string function, int n)
+	: m_code{code}
+	, m_function{std::move(function)}
+	, m_error{error(n)}
+{
+}
+
+Error::Error(Code code, std::string function, std::string error)
+	: m_code{code}
+	, m_function{std::move(function)}
+	, m_error{std::move(error)}
+{
+}
+
+/* }}} */
+
+/*
+ * Predefine addressed to be used
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Addresses */
+
+namespace address {
+
+/* Default domain */
+int Ip::m_default{AF_INET};
+
+Ip::Ip(Type domain) noexcept
+	: m_domain(static_cast<int>(domain))
+{
+	assert(m_domain == AF_INET6 || m_domain == AF_INET);
+
+	if (m_domain == AF_INET6) {
+		std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+	} else {
+		std::memset(&m_sin, 0, sizeof (sockaddr_in));
+	}
+}
+
+Ip::Ip(const std::string &host, int port, Type domain)
+	: m_domain(static_cast<int>(domain))
+{
+	assert(m_domain == AF_INET6 || m_domain == AF_INET);
+
+	if (host == "*") {
+		if (m_domain == AF_INET6) {
+			std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+
+			m_length = sizeof (sockaddr_in6);
+			m_sin6.sin6_addr = in6addr_any;
+			m_sin6.sin6_family = AF_INET6;
+			m_sin6.sin6_port = htons(port);
+		} else {
+			std::memset(&m_sin, 0, sizeof (sockaddr_in));
+
+			m_length = sizeof (sockaddr_in);
+			m_sin.sin_addr.s_addr = INADDR_ANY;
+			m_sin.sin_family = AF_INET;
+			m_sin.sin_port = htons(port);
+		}
+	} else {
+		addrinfo hints, *res;
+
+		std::memset(&hints, 0, sizeof (addrinfo));
+		hints.ai_family = domain;
+
+		auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
+		if (error != 0) {
+			throw Error{Error::System, "getaddrinfo", gai_strerror(error)};
+		}
+
+		if (m_domain == AF_INET6) {
+			std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen);
+		} else {
+			std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen);
+		}
+
+		m_length = res->ai_addrlen;
+		freeaddrinfo(res);
+	}
+}
+
+Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept
+	: m_length{length}
+	, m_domain{ss->ss_family}
+{
+	assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET);
+
+	if (ss->ss_family == AF_INET6) {
+		std::memcpy(&m_sin6, ss, length);
+	} else if (ss->ss_family == AF_INET) {
+		std::memcpy(&m_sin, ss, length);
+	}
+}
+
+#if !defined(_WIN32)
+
+Local::Local() noexcept
+{
+	std::memset(&m_sun, 0, sizeof (sockaddr_un));
+}
+
+Local::Local(std::string path, bool rm) noexcept
+	: m_path{std::move(path)}
+{
+	/* Silently remove the file even if it fails */
+	if (rm) {
+		::remove(m_path.c_str());
+	}
+
+	/* Copy the path */
+	std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path));
+	std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1);
+
+	/* Set the parameters */
+	m_sun.sun_family = AF_LOCAL;
+}
+
+Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept
+{
+	assert(ss->ss_family == AF_LOCAL);
+
+	if (ss->ss_family == AF_LOCAL) {
+		std::memcpy(&m_sun, ss, length);
+		m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path;
+	}
+}
+
+#endif // !_WIN32
+
+} // !address
+
+/* }}} */
+
+/*
+ * Select
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Select */
+
+std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms)
+{
+	timeval maxwait, *towait;
+	fd_set readset;
+	fd_set writeset;
+
+	FD_ZERO(&readset);
+	FD_ZERO(&writeset);
+
+	Handle max = 0;
+
+	for (const auto &pair : table) {
+		if ((pair.second & Condition::Readable) == Condition::Readable) {
+			FD_SET(pair.first, &readset);
+		}
+		if ((pair.second & Condition::Writable) == Condition::Writable) {
+			FD_SET(pair.first, &writeset);
+		}
+
+		if (pair.first > max) {
+			max = pair.first;
+		}
+	}
+
+	maxwait.tv_sec = 0;
+	maxwait.tv_usec = ms * 1000;
+
+	// Set to nullptr for infinite timeout.
+	towait = (ms < 0) ? nullptr : &maxwait;
+
+	auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
+	if (error == Failure) {
+		throw Error{Error::System, "select"};
+	}
+	if (error == 0) {
+		throw Error{Error::Timeout, "select", TIMEOUT_MSG};
+	}
+
+	std::vector<ListenerStatus> sockets;
+
+	for (const auto &pair : table) {
+		if (FD_ISSET(pair.first, &readset)) {
+			sockets.push_back(ListenerStatus{pair.first, Condition::Readable});
+		}
+		if (FD_ISSET(pair.first, &writeset)) {
+			sockets.push_back(ListenerStatus{pair.first, Condition::Writable});
+		}
+	}
+
+	return sockets;
+}
+
+/* }}} */
+
+/*
+ * Poll
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Poll */
+
+/*
+ * Poll implementation
+ * ------------------------------------------------------------------
+ */
+
+#if defined(SOCKET_HAVE_POLL)
+
+#if defined(_WIN32)
+#  define poll WSAPoll
+#endif
+
+short Poll::toPoll(Condition condition) const noexcept
+{
+	short result(0);
+
+	if ((condition & Condition::Readable) == Condition::Readable) {
+		result |= POLLIN;
+	}
+	if ((condition & Condition::Writable) == Condition::Writable) {
+		result |= POLLOUT;
+	}
+
+	return result;
+}
+
+Condition Poll::toCondition(short &event) const noexcept
+{
+	Condition condition{Condition::None};
+
+	/*
+	 * Poll implementations mark the socket differently regarding
+	 * the disconnection of a socket.
+	 *
+	 * At least, even if POLLHUP or POLLIN is set, recv() always
+	 * return 0 so we mark the socket as readable.
+	 */
+	if ((event & POLLIN) || (event & POLLHUP)) {
+		condition |= Condition::Readable;
+	}
+	if (event & POLLOUT) {
+		condition |= Condition::Writable;
+	}
+
+	/* Reset event for safety */
+	event = 0;
+
+	return condition;
+}
+
+void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add)
+{
+	if (add) {
+		m_fds.push_back(pollfd{h, toPoll(condition), 0});
+	} else {
+		auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
+			return pfd.fd == h;
+		});
+
+		it->events |= toPoll(condition);
+	}
+}
+
+void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
+{
+	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
+		return pfd.fd == h;
+	});
+
+	if (remove) {
+		m_fds.erase(it);
+	} else {
+		it->events &= ~(toPoll(condition));
+	}
+}
+
+std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms)
+{
+	auto result = poll(m_fds.data(), m_fds.size(), ms);
+	if (result == 0) {
+		throw Error{Error::Timeout, "select", TIMEOUT_MSG};
+	}
+	if (result < 0) {
+		throw Error{Error::System, "poll"};
+	}
+
+	std::vector<ListenerStatus> sockets;
+	for (auto &fd : m_fds) {
+		if (fd.revents != 0) {
+			sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)});
+		}
+	}
+
+	return sockets;
+}
+
+#endif // !SOCKET_HAVE_POLL
+
+/* }}} */
+
+/*
+ * Epoll implementation
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Epoll */
+
+#if defined(SOCKET_HAVE_EPOLL)
+
+uint32_t Epoll::toEpoll(Condition condition) const noexcept
+{
+	uint32_t events = 0;
+
+	if ((condition & Condition::Readable) == Condition::Readable) {
+		events |= EPOLLIN;
+	}
+	if ((condition & Condition::Writable) == Condition::Writable) {
+		events |= EPOLLOUT;
+	}
+
+	return events;
+}
+
+Condition Epoll::toCondition(uint32_t events) const noexcept
+{
+	Condition condition{Condition::None};
+
+	if ((events & EPOLLIN) || (events & EPOLLHUP)) {
+		condition |= Condition::Readable;
+	}
+	if (events & EPOLLOUT) {
+		condition |= Condition::Writable;
+	}
+
+	return condition;
+}
+
+void Epoll::update(Handle h, int op, int eflags)
+{
+	epoll_event ev;
+
+	std::memset(&ev, 0, sizeof (epoll_event));
+
+	ev.events = eflags;
+	ev.data.fd = h;
+
+	if (epoll_ctl(m_handle, op, h, &ev) < 0) {
+		throw Error{Error::System, "epoll_ctl"};
+	}
+}
+
+Epoll::Epoll()
+	: m_handle{epoll_create1(0)}
+{
+	if (m_handle < 0) {
+		throw Error{Error::System, "epoll_create"};
+	}
+}
+
+Epoll::~Epoll()
+{
+	close(m_handle);
+}
+
+/*
+ * For set and unset, we need to apply the whole flags required, so if the socket
+ * was set to Connection::Readable and user add Connection::Writable, we must
+ * place both.
+ */
+void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add)
+{
+	if (add) {
+		update(sc, EPOLL_CTL_ADD, toEpoll(condition));
+		m_events.resize(m_events.size() + 1);
+	} else {
+		update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition));
+	}
+}
+
+/*
+ * Unset is a bit complicated case because Listener tells us which
+ * flag to remove but to update epoll descriptor we need to pass
+ * the effective flags that we want to be applied.
+ *
+ * So we put the same flags that are currently effective and remove the
+ * requested one.
+ */
+void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove)
+{
+	if (remove) {
+		update(sc, EPOLL_CTL_DEL, 0);
+		m_events.resize(m_events.size() - 1);
+	} else {
+		update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition)));
+	}
+}
+
+std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms)
+{
+	int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
+	std::vector<ListenerStatus> result;
+
+	if (ret == 0) {
+		throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG};
+	}
+	if (ret < 0) {
+		throw Error{Error::System, "epoll_wait"};
+	}
+
+	for (int i = 0; i < ret; ++i) {
+		result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
+	}
+
+	return result;
+}
+
+#endif // !SOCKET_HAVE_EPOLL
+
+/* }}} */
+
+/*
+ * Kqueue implementation
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Kqueue */
+
+#if defined(SOCKET_HAVE_KQUEUE)
+
+Kqueue::Kqueue()
+	: m_handle(kqueue())
+{
+	if (m_handle < 0) {
+		throw Error{Error::System, "kqueue"};
+	}
+}
+
+Kqueue::~Kqueue()
+{
+	close(m_handle);
+}
+
+void Kqueue::update(Handle h, int filter, int kflags)
+{
+	struct kevent ev;
+
+	EV_SET(&ev, h, filter, kflags, 0, 0, nullptr);
+
+	if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) {
+		throw Error{Error::System, "kevent"};
+	}
+}
+
+void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add)
+{
+	if ((condition & Condition::Readable) == Condition::Readable) {
+		update(h, EVFILT_READ, EV_ADD | EV_ENABLE);
+	}
+	if ((condition & Condition::Writable) == Condition::Writable) {
+		update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE);
+	}
+
+	if (add) {
+		m_result.resize(m_result.size() + 1);
+	}
+}
+
+void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
+{
+	if ((condition & Condition::Readable) == Condition::Readable) {
+		update(h, EVFILT_READ, EV_DELETE);
+	}
+	if ((condition & Condition::Writable) == Condition::Writable) {
+		update(h, EVFILT_WRITE, EV_DELETE);
+	}
+
+	if (remove) {
+		m_result.resize(m_result.size() - 1);
+	}
+}
+
+std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms)
+{
+	std::vector<ListenerStatus> sockets;
+	timespec ts = { 0, 0 };
+	timespec *pts = (ms <= 0) ? nullptr : &ts;
+
+	ts.tv_sec = ms / 1000;
+	ts.tv_nsec = (ms % 1000) * 1000000;
+
+	int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts);
+
+	if (nevents == 0) {
+		throw Error{Error::Timeout, "kevent", TIMEOUT_MSG};
+	}
+	if (nevents < 0) {
+		throw Error{Error::System, "kevent"};
+	}
+
+	for (int i = 0; i < nevents; ++i) {
+		sockets.push_back(ListenerStatus{
+			static_cast<Handle>(m_result[i].ident),
+			m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable
+		});
+	}
+
+	return sockets;
+}
+
+#endif // !SOCKET_HAVE_KQUEUE
+
+/* }}} */
+
+} // !net
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/sockets.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,4078 @@
+/*
+ * sockets.h -- portable C++ socket wrappers
+ *
+ * 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 _SOCKETS_H_
+#define _SOCKETS_H_
+
+/**
+ * @file Sockets.h
+ * @brief Portable socket abstraction
+ *
+ * # Introduction
+ *
+ * This file is a portable networking library.
+ *
+ * ## Options
+ *
+ * The user may set the following variables before compiling these files:
+ *
+ * ### General options
+ *
+ * - **SOCKET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
+ * automatically calls net::init function and net::finish functions.
+ * - **SOCKET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL library.
+ * - **SOCKET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class with Tls to automatically init
+ * the OpenSSL library. You will need to call net::ssl::init and net::ssl::finish.
+ *
+ * ### Options for Listener class
+ *
+ * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll.
+ *
+ * We assume that `select(2)` is always available.
+ *
+ * Of course, you can set the variables yourself if you test it with your build system.
+ *
+ * - **SOCKET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows
+ *   if _WIN32_WINNT is set to 0x0600 or greater.
+ * - **SOCKET_HAVE_KQUEUE**: Defined on all BSD and Apple.
+ * - **SOCKET_HAVE_EPOLL**: Defined on Linux only.
+ * - **SOCKET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`).
+ *
+ * The preference priority is ordered from left to right.
+ *
+ * | System        | Backend                 | Class name   |
+ * |---------------|-------------------------|--------------|
+ * | Linux         | epoll(7)                | Epoll        |
+ * | *BSD          | kqueue(2)               | Kqueue       |
+ * | Windows       | poll(2), select(2)      | Poll, Select |
+ * | Mac OS X      | kqueue(2)               | Kqueue       |
+ */
+
+#if defined(_WIN32)
+#  if _WIN32_WINNT >= 0x0600 && !defined(SOCKET_HAVE_POLL)
+#    define SOCKET_HAVE_POLL
+#  endif
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#  if !defined(SOCKET_HAVE_KQUEUE)
+#    define SOCKET_HAVE_KQUEUE
+#  endif
+#  if !defined(SOCKET_HAVE_POLL)
+#    define SOCKET_HAVE_POLL
+#  endif
+#elif defined(__linux__)
+#  if !defined(SOCKET_HAVE_EPOLL)
+#    define SOCKET_HAVE_EPOLL
+#  endif
+#  if !defined(SOCKET_HAVE_POLL)
+#    define SOCKET_HAVE_POLL
+#  endif
+#endif
+
+/*
+ * Define SOCKET_DEFAULT_BACKEND
+ * ------------------------------------------------------------------
+ */
+
+/**
+ * Defines the default Listener backend to use.
+ *
+ * @note Do not rely on the value shown in doxygen.
+ */
+#if defined(_WIN32)
+#  if !defined(SOCKET_DEFAULT_BACKEND)
+#    if defined(SOCKET_HAVE_POLL)
+#      define SOCKET_DEFAULT_BACKEND Poll
+#    else
+#      define SOCKET_DEFAULT_BACKEND Select
+#    endif
+#  endif
+#elif defined(__linux__)
+#  include <sys/epoll.h>
+
+#  if !defined(SOCKET_DEFAULT_BACKEND)
+#    define SOCKET_DEFAULT_BACKEND Epoll
+#  endif
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__)
+#  include <sys/types.h>
+#  include <sys/event.h>
+#  include <sys/time.h>
+
+#  if !defined(SOCKET_DEFAULT_BACKEND)
+#    define SOCKET_DEFAULT_BACKEND Kqueue
+#  endif
+#else
+#  if !defined(SOCKET_DEFAULT_BACKEND)
+#    define SOCKET_DEFAULT_BACKEND Select
+#  endif
+#endif
+
+#if defined(SOCKET_HAVE_POLL) && !defined(_WIN32)
+#  include <poll.h>
+#endif
+
+/*
+ * Headers to include
+ * ------------------------------------------------------------------
+ */
+
+#if defined(_WIN32)
+#  include <cstdlib>
+
+#  include <WinSock2.h>
+#  include <WS2tcpip.h>
+#else
+#  include <cerrno>
+
+#  include <sys/ioctl.h>
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <sys/un.h>
+
+#  include <arpa/inet.h>
+
+#  include <netinet/in.h>
+#  include <netinet/tcp.h>
+
+#  include <fcntl.h>
+#  include <netdb.h>
+#  include <unistd.h>
+#endif
+
+#if !defined(SOCKET_NO_SSL)
+#  include <openssl/err.h>
+#  include <openssl/evp.h>
+#  include <openssl/ssl.h>
+#endif
+
+#include <cassert>
+#include <chrono>
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * General network namespace.
+ */
+namespace net {
+
+/*
+ * Portables types
+ * ------------------------------------------------------------------
+ *
+ * The following types are defined differently between Unix and Windows.
+ */
+
+/* {{{ Protocols */
+
+#if defined(_WIN32)
+
+/**
+ * Socket type, SOCKET.
+ */
+using Handle	= SOCKET;
+
+/**
+ * Argument to pass to set.
+ */
+using ConstArg	= const char *;
+
+/**
+ * Argument to pass to get.
+ */
+using Arg	= char *;
+
+#else
+
+/**
+ * Socket type, int.
+ */
+using Handle	= int;
+
+/**
+ * Argument to pass to set.
+ */
+using ConstArg	= const void *;
+
+/**
+ * Argument to pass to get.
+ */
+using Arg	= void *;
+
+#endif
+
+/* }}} */
+
+/*
+ * Portable constants
+ * ------------------------------------------------------------------
+ *
+ * These constants are needed to check functions return codes, they are rarely needed in end user code.
+ */
+
+/* {{{ Constants */
+
+/*
+ * The following constants are defined differently from Unix
+ * to Windows.
+ */
+#if defined(_WIN32)
+
+/**
+ * Socket creation failure or invalidation.
+ */
+extern const Handle Invalid;
+
+/**
+ * Socket operation failure.
+ */
+extern const int Failure;
+
+#else
+
+/**
+ * Socket creation failure or invalidation.
+ */
+extern const int Invalid;
+
+/**
+ * Socket operation failure.
+ */
+extern const int Failure;
+
+#endif
+
+#if !defined(SOCKET_NO_SSL)
+
+namespace ssl {
+
+/**
+ * @enum Method
+ * @brief Which OpenSSL method to use.
+ */
+enum Method {
+	Tlsv1,		//!< TLS v1.2 (recommended)
+	Sslv3		//!< SSLv3
+};
+
+} // !ssl
+
+#endif
+
+/* }}} */
+
+/*
+ * Portable functions
+ * ------------------------------------------------------------------
+ *
+ * The following free functions can be used to initialize the library or to get the last system error.
+ */
+
+/* {{{ Functions */
+
+/**
+ * Initialize the socket library. Except if you defined SOCKET_NO_AUTO_INIT, you don't need to call this
+ * function manually.
+ */
+void init() noexcept;
+
+/**
+ * Close the socket library.
+ */
+void finish() noexcept;
+
+#if !defined(SOCKET_NO_SSL)
+
+/**
+ * OpenSSL namespace.
+ */
+namespace ssl {
+
+/**
+ * Initialize the OpenSSL library. Except if you defined SOCKET_NO_AUTO_SSL_INIT, you don't need to call this function
+ * manually.
+ */
+void init() noexcept;
+
+/**
+ * Close the OpenSSL library.
+ */
+void finish() noexcept;
+
+} // !ssl
+
+#endif // SOCKET_NO_SSL
+
+/**
+ * Get the last socket system error. The error is set from errno or from
+ * WSAGetLastError on Windows.
+ *
+ * @return a string message
+ */
+std::string error();
+
+/**
+ * Get the last system error.
+ *
+ * @param errn the error number (errno or WSAGetLastError)
+ * @return the error
+ */
+std::string error(int errn);
+
+/* }}} */
+
+/*
+ * Error class
+ * ------------------------------------------------------------------
+ *
+ * This is the main exception thrown on socket operations.
+ */
+
+/* {{{ Error */
+
+/**
+ * @class Error
+ * @brief Base class for sockets error
+ */
+class Error : public std::exception {
+public:
+	/**
+	 * @enum Code
+	 * @brief Which kind of error
+	 */
+	enum Code {
+		Timeout,		///!< The action did timeout
+		System,			///!< There is a system error
+		Other			///!< Other custom error
+	};
+
+private:
+	Code m_code;
+	std::string m_function;
+	std::string m_error;
+
+public:
+	/**
+	 * Constructor that use the last system error.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 */
+	Error(Code code, std::string function);
+
+	/**
+	 * Constructor that use the system error set by the user.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 * @param error the error
+	 */
+	Error(Code code, std::string function, int error);
+
+	/**
+	 * Constructor that set the error specified by the user.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 * @param error the error
+	 */
+	Error(Code code, std::string function, std::string error);
+
+	/**
+	 * Get which function has triggered the error.
+	 *
+	 * @return the function name (e.g connect)
+	 */
+	inline const std::string &function() const noexcept
+	{
+		return m_function;
+	}
+
+	/**
+	 * The error code.
+	 *
+	 * @return the code
+	 */
+	inline Code code() const noexcept
+	{
+		return m_code;
+	}
+
+	/**
+	 * Get the error (only the error content).
+	 *
+	 * @return the error
+	 */
+	const char *what() const noexcept
+	{
+		return m_error.c_str();
+	}
+};
+
+/* }}} */
+
+/*
+ * State class
+ * ------------------------------------------------------------------
+ *
+ * To facilitate higher-level stuff, the socket has a state.
+ */
+
+/* {{{ State */
+
+/**
+ * @enum State
+ * @brief Current socket state.
+ */
+enum class State {
+	Open,			//!< Socket is open
+	Bound,			//!< Socket is bound to an address
+	Connecting,		//!< The connection is in progress
+	Connected,		//!< Connection is complete
+	Accepted,		//!< Socket has been accepted (client)
+	Accepting,		//!< The client acceptation is in progress
+	Closed,			//!< The socket has been closed
+	Disconnected,		//!< The connection was lost
+};
+
+/* }}} */
+
+/*
+ * Action enum
+ * ------------------------------------------------------------------
+ *
+ * Defines the pending operation.
+ */
+
+/* {{{ Action */
+
+/**
+ * @enum Action
+ * @brief Define the current operation that must complete.
+ *
+ * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket
+ * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the
+ * operation is complete.
+ *
+ * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is
+ * met.
+ *
+ * It is important to complete the operation in the correct order because protocols like Tls may require to continue
+ * re-negociating when calling Socket::send or Socket::Recv.
+ */
+enum class Action {
+	None,		//!< No action is required, socket is ready
+	Accept,		//!< The socket is not yet accepted, caller must call accept() again
+	Connect,	//!< The socket is not yet connected, caller must call connect() again
+	Receive,	//!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again
+	Send		//!< The send operation has not succeded yet, caller must call send() or sendto() again
+};
+
+/* }}} */
+
+/*
+ * Condition enum
+ * ------------------------------------------------------------------
+ *
+ * Defines if we must wait for reading or writing.
+ */
+
+/* {{{ Condition */
+
+/**
+ * @enum Condition
+ * @brief Define the required condition for the socket.
+ *
+ * As explained in Action enumeration, some operations required to be called several times, before calling these
+ * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition.
+ */
+enum class Condition {
+	None,			//!< No condition is required
+	Readable = (1 << 0),	//!< The socket must be readable
+	Writable = (1 << 1)	//!< The socket must be writable
+};
+
+/**
+ * Apply bitwise XOR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator^(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise AND.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator&(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise OR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator|(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise NOT.
+ *
+ * @param v the value
+ * @return the complement
+ */
+constexpr Condition operator~(Condition v) noexcept
+{
+	return static_cast<Condition>(~static_cast<int>(v));
+}
+
+/**
+ * Assign bitwise OR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator|=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
+
+	return v1;
+}
+
+/**
+ * Assign bitwise AND.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator&=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
+
+	return v1;
+}
+
+/**
+ * Assign bitwise XOR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator^=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
+
+	return v1;
+}
+
+/* }}} */
+
+/*
+ * Base Socket class
+ * ------------------------------------------------------------------
+ *
+ * This base class has operations that are common to all types of sockets but you usually instanciate
+ * a SocketTcp or SocketUdp
+ */
+
+/* {{{ Socket */
+
+/**
+ * @class Socket
+ * @brief Base socket class for socket operations.
+ *
+ * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the
+ * underlying protocol for more details.
+ *
+ * @see protocol::Tls
+ * @see protocol::Tcp
+ * @see protocol::Udp
+ */
+template <typename Address, typename Protocol>
+class Socket {
+private:
+	Protocol m_proto;
+	State m_state{State::Closed};
+	Action m_action{Action::None};
+	Condition m_condition{Condition::None};
+
+protected:
+	/**
+	 * The native handle.
+	 */
+	Handle m_handle{Invalid};
+
+public:
+	/**
+	 * Create a socket handle.
+	 *
+	 * This is the primary function and the only one that creates the socket handle, all other constructors
+	 * are just overloaded functions.
+	 *
+	 * @param domain the domain AF_*
+	 * @param type the type SOCK_*
+	 * @param protocol the protocol
+	 * @param iface the implementation
+	 * @throw net::Error on errors
+	 * @post state is set to Open
+	 * @post handle is not set to Invalid
+	 */
+	Socket(int domain, int type, int protocol, Protocol iface = {})
+		: m_proto(std::move(iface))
+	{
+#if !defined(SOCKET_NO_AUTO_INIT)
+		init();
+#endif
+		m_handle = ::socket(domain, type, protocol);
+
+		if (m_handle == Invalid) {
+			throw Error{Error::System, "socket"};
+		}
+
+		m_proto.create(*this);
+		m_state = State::Open;
+
+		assert(m_handle != Invalid);
+	}
+
+	/**
+	 * This tries to create a socket.
+	 *
+	 * Domain and type are determined by the Address and Protocol object.
+	 *
+	 * @param protocol the protocol
+	 * @param address which type of address
+	 * @throw net::Error on errors
+	 */
+	explicit inline Socket(Protocol protocol = {}, const Address &address = {})
+		: Socket{address.domain(), protocol.type(), 0, std::move(protocol)}
+	{
+	}
+
+	/**
+	 * Construct a socket with an already created descriptor.
+	 *
+	 * @param handle the native descriptor
+	 * @param state specify the socket state
+	 * @param protocol the type of socket implementation
+	 * @post action is set to None
+	 * @post condition is set to None
+	 */
+	explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept
+		: m_proto(std::move(protocol))
+		, m_state{state}
+		, m_handle{handle}
+	{
+		assert(m_action == Action::None);
+		assert(m_condition == Condition::None);
+	}
+
+	/**
+	 * Create an invalid socket. Can be used when you cannot instanciate the socket immediately.
+	 */
+	explicit inline Socket(std::nullptr_t) noexcept
+		: m_handle{Invalid}
+	{
+	}
+
+	/**
+	 * Copy constructor deleted.
+	 */
+	Socket(const Socket &) = delete;
+
+	/**
+	 * Transfer ownership from other to this.
+	 *
+	 * @param other the other socket
+	 */
+	inline Socket(Socket &&other) noexcept
+		: m_proto(std::move(other.m_proto))
+		, m_state{other.m_state}
+		, m_action{other.m_action}
+		, m_condition{other.m_condition}
+		, m_handle{other.m_handle}
+	{
+		/* Invalidate other */
+		other.m_handle = Invalid;
+		other.m_state = State::Closed;
+		other.m_action = Action::None;
+		other.m_condition = Condition::None;
+	}
+
+	/**
+	 * Default destructor.
+	 */
+	virtual ~Socket()
+	{
+		close();
+	}
+
+	/**
+	 * Access the implementation.
+	 *
+	 * @return the implementation
+	 * @warning use this function with care
+	 */
+	inline const Protocol &protocol() const noexcept
+	{
+		return m_proto;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the implementation
+	 */
+	inline Protocol &protocol() noexcept
+	{
+		return m_proto;
+	}
+
+	/**
+	 * Get the current socket state.
+	 *
+	 * @return the state
+	 */
+	inline State state() const noexcept
+	{
+		return m_state;
+	}
+
+	/**
+	 * Change the current socket state.
+	 *
+	 * @param state the new state
+	 * @warning only implementations should call this function
+	 */
+	inline void setState(State state) noexcept
+	{
+		m_state = state;
+	}
+
+	/**
+	 * Get the pending operation.
+	 *
+	 * @return the action to complete before continuing
+	 * @note usually only needed in non-blocking sockets
+	 */
+	inline Action action() const noexcept
+	{
+		return m_action;
+	}
+
+	/**
+	 * Change the pending operation.
+	 *
+	 * @param action the action
+	 * @warning you should not call this function yourself
+	 */
+	inline void setAction(Action action) noexcept
+	{
+		m_action = action;
+	}
+
+	/**
+	 * Get the condition to wait for.
+	 *
+	 * @return the condition
+	 */
+	inline Condition condition() const noexcept
+	{
+		return m_condition;
+	}
+
+	/**
+	 * Change the condition required.
+	 *
+	 * @param condition the condition
+	 * @warning you should not call this function yourself
+	 */
+	inline void setCondition(Condition condition) noexcept
+	{
+		m_condition = condition;
+	}
+
+	/**
+	 * Set an option for the socket. Wrapper of setsockopt(2).
+	 *
+	 * @param level the setting level
+	 * @param name the name
+	 * @param arg the value
+	 * @throw net::Error on errors
+	 */
+	template <typename Argument>
+	void set(int level, int name, const Argument &arg)
+	{
+		if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) {
+			throw Error{Error::System, "set"};
+		}
+	}
+
+	/**
+	 * Object-oriented option setter.
+	 *
+	 * The object must have `set(Socket<Address, Protocol> &) const`.
+	 *
+	 * @param option the option
+	 * @throw net::Error on errors
+	 */
+	template <typename Option>
+	inline void set(const Option &option)
+	{
+		option.set(*this);
+	}
+
+	/**
+	 * Get an option for the socket. Wrapper of getsockopt(2).
+	 *
+	 * @param level the setting level
+	 * @param name the name
+	 * @throw net::Error on errors
+	 */
+	template <typename Argument>
+	Argument get(int level, int name)
+	{
+		Argument desired, result{};
+		socklen_t size = sizeof (result);
+
+		if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) {
+			throw Error{Error::System, "get"};
+		}
+
+		std::memcpy(&result, &desired, size);
+
+		return result;
+	}
+
+	/**
+	 * Object-oriented option getter.
+	 *
+	 * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value
+	 * returned from this function.
+	 *
+	 * @return the same value as get() in the option
+	 * @throw net::Error on errors
+	 */
+	template <typename Option>
+	inline auto get() -> decltype(std::declval<Option>().get(*this))
+	{
+		return Option{}.get(*this);
+	}
+
+	/**
+	 * Get the native handle.
+	 *
+	 * @return the handle
+	 * @warning Not portable
+	 */
+	inline Handle handle() const noexcept
+	{
+		return m_handle;
+	}
+
+	/**
+	 * Bind using a native address.
+	 *
+	 * @param address the address
+	 * @param length the size
+	 * @pre state must not be Bound
+	 * @throw net::Error on errors
+	 */
+	void bind(const sockaddr *address, socklen_t length)
+	{
+		assert(m_state != State::Bound);
+
+		if (::bind(m_handle, address, length) == Failure) {
+			throw Error{Error::System, "bind"};
+		}
+
+		m_state = State::Bound;
+	}
+
+	/**
+	 * Overload that takes an address.
+	 *
+	 * @param address the address
+	 * @throw net::Error on errors
+	 */
+	inline void bind(const Address &address)
+	{
+		bind(address.address(), address.length());
+	}
+
+	/**
+	 * Listen for pending connection.
+	 *
+	 * @param max the maximum number
+	 * @pre state must be Bound
+	 * @throw net::Error on errors
+	 */
+	inline void listen(int max = 128)
+	{
+		assert(m_state == State::Bound);
+
+		if (::listen(this->m_handle, max) == Failure) {
+			throw Error{Error::System, "listen"};
+		}
+	}
+
+	/**
+	 * Connect to the address.
+	 *
+	 * If connection cannot be established immediately, connect with no argument must be called again. See
+	 * the underlying protocol for more information.
+	 *
+	 * @pre state must be State::Open
+	 * @param address the address
+	 * @param length the the address length
+	 * @throw net::Error on errors
+	 * @post state is set to State::Connecting or State::Connected
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	void connect(const sockaddr *address, socklen_t length)
+	{
+		assert(m_state == State::Open);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		m_proto.connect(*this, address, length);
+
+		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
+		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * Effectively call connect(address.address(), address.length());
+	 *
+	 * @param address the address
+	 */
+	inline void connect(const Address &address)
+	{
+		connect(address.address(), address.length());
+	}
+
+	/**
+	 * Continue the connection, only required with non-blocking sockets.
+	 *
+	 * @pre state must be State::Connecting
+	 * @throw net::Error on errors
+	 */
+	void connect()
+	{
+		assert(m_state == State::Connecting);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		m_proto.connect(*this);
+
+		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
+		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
+	}
+
+	/**
+	 * Accept a new client. If there are no pending connection, throws an error.
+	 *
+	 * If the client cannot be accepted immediately, the client is returned and accept with no arguments
+	 * must be called on it. See the underlying protocol for more information.
+	 *
+	 * @pre state must be State::Bound
+	 * @param info the address where to store client's information (optional)
+	 * @return the new socket
+	 * @throw Error on errors
+	 * @post returned client's state is set to State::Accepting or State::Accepted
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	Socket<Address, Protocol> accept(Address *info)
+	{
+		assert(m_state == State::Bound);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		sockaddr_storage storage;
+		socklen_t length = sizeof (storage);
+
+		Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length);
+
+		if (info) {
+			*info = Address{&storage, length};
+		}
+
+		/* Master do not change */
+		assert(m_state == State::Bound);
+		assert(m_action == Action::None);
+		assert(m_condition == Condition::None);
+
+		/* Client */
+		assert(
+			(sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) ||
+			(sc.state() == State::Accepted  && sc.action() == Action::None   && sc.condition() == Condition::None)
+		);
+
+		return sc;
+	}
+
+	/**
+	 * Continue the accept process on this client. This function must be called only when the socket is
+	 * ready to be readable or writable! (see condition).
+	 *
+	 * @pre state must be State::Accepting
+	 * @throw Error on errors
+	 * @post if connection is complete, state is changed to State::Accepted, action and condition are unset
+	 * @post if connection is still in progress, condition is set
+	 */
+	void accept()
+	{
+		assert(m_state == State::Accepting);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		m_proto.accept(*this);
+
+		assert(
+			(m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) ||
+			(m_state == State::Accepted  && m_action == Action::None   && m_condition == Condition::None)
+		);
+	}
+
+	/**
+	 * Get the local name. This is a wrapper of getsockname().
+	 *
+	 * @return the address
+	 * @throw Error on failures
+	 * @pre state() must not be State::Closed
+	 */
+	Address address() const
+	{
+		assert(m_state != State::Closed);
+
+		sockaddr_storage ss;
+		socklen_t length = sizeof (sockaddr_storage);
+
+		if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) {
+			throw Error{Error::System, "getsockname"};
+		}
+
+		return Address(&ss, length);
+	}
+
+	/**
+	 * Receive some data.
+	 *
+	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
+	 * again when ready. See the underlying protocol for more information.
+	 *
+	 * If action is set to Action::None and result is set to 0, disconnection occured.
+	 *
+	 * @param data the destination buffer
+	 * @param length the buffer length
+	 * @pre action must not be Action::Send
+	 * @return the number of bytes received or 0
+	 * @throw Error on error
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	unsigned recv(void *data, unsigned length)
+	{
+		assert(m_action != Action::Send);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		unsigned nbread = m_proto.recv(*this, data, length);
+
+		return nbread;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param count the number of bytes to receive
+	 * @return the string
+	 * @throw Error on error
+	 */
+	inline std::string recv(unsigned count)
+	{
+		std::string result;
+
+		result.resize(count);
+		auto n = recv(const_cast<char *>(result.data()), count);
+		result.resize(n);
+
+		return result;
+	}
+
+	/**
+	 * Send some data.
+	 *
+	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
+	 * again when ready. See the underlying protocol for more information.
+	 *
+	 * @param data the data buffer
+	 * @param length the buffer length
+	 * @return the number of bytes sent or 0
+	 * @pre action() must not be Flag::Receive
+	 * @throw Error on error
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	unsigned send(const void *data, unsigned length)
+	{
+		assert(m_action != Action::Receive);
+
+		m_action = Action::None;
+		m_condition = Condition::None;
+
+		unsigned nbsent = m_proto.send(*this, data, length);
+
+		assert((m_action == Action::None && m_condition == Condition::None) ||
+		       (m_action == Action::Send && m_condition != Condition::None));
+
+		return nbsent;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param data the string to send
+	 * @return the number of bytes sent
+	 * @throw Error on error
+	 */
+	inline unsigned send(const std::string &data)
+	{
+		return send(data.c_str(), data.size());
+	}
+
+	/**
+	 * Send data to an end point.
+	 *
+	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
+	 * again when ready. See the underlying protocol for more information.
+	 *
+	 * @param data the buffer
+	 * @param length the buffer length
+	 * @param address the client address
+	 * @param addrlen the address length
+	 * @return the number of bytes sent
+	 * @throw net::Error on errors
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+	{
+		return m_proto.sendto(*this, data, length, address, addrlen);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param data the buffer
+	 * @param length the buffer length
+	 * @param address the destination
+	 * @return the number of bytes sent
+	 * @throw net::Error on errors
+	 */
+	inline unsigned sendto(const void *data, unsigned length, const Address &address)
+	{
+		return sendto(data, length, address.address(), address.length());
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param data the data
+	 * @param address the address
+	 * @return the number of bytes sent
+	 * @throw net:;Error on errors
+	 */
+	inline unsigned sendto(const std::string &data, const Address &address)
+	{
+		return sendto(data.c_str(), data.length(), address);
+	}
+
+	/**
+	 * Receive data from an end point.
+	 *
+	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
+	 * again when ready. See the underlying protocol for more information.
+	 *
+	 * @param data the destination buffer
+	 * @param length the buffer length
+	 * @param address the address destination
+	 * @param addrlen the address length (in/out)
+	 * @return the number of bytes received
+	 * @throw net::Error on errors
+	 * @note For non-blocking sockets, see the underlying protocol function for more details
+	 */
+	inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+	{
+		return m_proto.recvfrom(*this, data, length, address, addrlen);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param data the destination buffer
+	 * @param length the buffer length
+	 * @param info the address destination
+	 * @return the number of bytes received
+	 * @throw net::Error on errors
+	 */
+	inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr)
+	{
+		sockaddr_storage storage;
+		socklen_t addrlen = sizeof (sockaddr_storage);
+
+		auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen);
+
+		if (info && n != 0) {
+			*info = Address{&storage, addrlen};
+		}
+
+		return n;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param count the maximum number of bytes to receive
+	 * @param info the client information
+	 * @return the string
+	 * @throw net::Error on errors
+	 */
+	std::string recvfrom(unsigned count, Address *info = nullptr)
+	{
+		std::string result;
+
+		result.resize(count);
+		auto n = recvfrom(const_cast<char *>(result.data()), count, info);
+		result.resize(n);
+
+		return result;
+	}
+
+	/**
+	 * Close the socket.
+	 *
+	 * Automatically called from the destructor.
+	 */
+	void close()
+	{
+		if (m_handle != Invalid) {
+#if defined(_WIN32)
+			::closesocket(m_handle);
+#else
+			::close(m_handle);
+#endif
+			m_handle = Invalid;
+		}
+
+		m_state = State::Closed;
+		m_action = Action::None;
+		m_condition = Condition::None;
+	}
+
+	/**
+	 * Assignment operator forbidden.
+	 *
+	 * @return *this
+	 */
+	Socket &operator=(const Socket &) = delete;
+
+	/**
+	 * Transfer ownership from other to this. The other socket is left
+	 * invalid and will not be closed.
+	 *
+	 * @param other the other socket
+	 * @return this
+	 */
+	Socket &operator=(Socket &&other) noexcept
+	{
+		m_handle = other.m_handle;
+		m_proto = std::move(other.m_proto);
+		m_state = other.m_state;
+		m_action = other.m_action;
+		m_condition = other.m_condition;
+
+		/* Invalidate other */
+		other.m_handle = Invalid;
+		other.m_state = State::Closed;
+		other.m_action = Action::None;
+		other.m_condition = Condition::None;
+
+		return *this;
+	}
+};
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if they equals
+ */
+template <typename Address, typename Protocol>
+bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() == s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if they are different
+ */
+template <typename Address, typename Protocol>
+bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() != s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 < s2
+ */
+template <typename Address, typename Protocol>
+bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() < s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 > s2
+ */
+template <typename Address, typename Protocol>
+bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() > s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 <= s2
+ */
+template <typename Address, typename Protocol>
+bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() <= s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 >= s2
+ */
+template <typename Address, typename Protocol>
+bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() >= s2.handle();
+}
+
+/* }}} */
+
+/*
+ * Predefined options
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Options */
+
+/**
+ * Namespace of predefined options.
+ */
+namespace option {
+
+/*
+ * Options for socket
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Options for socket */
+
+/**
+ * @class SockBlockMode
+ * @brief Set or get the blocking-mode for a socket.
+ * @warning On Windows, it's not possible to check if the socket is blocking or not.
+ */
+class SockBlockMode {
+public:
+	/**
+	 * Set to false if you want non-blocking socket.
+	 */
+	bool value{false};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	void set(Socket<Address, Protocol> &sc) const
+	{
+#if defined(O_NONBLOCK) && !defined(_WIN32)
+		int flags;
+
+		if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) {
+			flags = 0;
+		}
+
+		if (value) {
+			flags &= ~(O_NONBLOCK);
+		} else {
+			flags |= O_NONBLOCK;
+		}
+
+		if (fcntl(sc.handle(), F_SETFL, flags) < 0) {
+			throw Error{Error::System, "fcntl"};
+		}
+#else
+		unsigned long flags = (value) ? 0 : 1;
+
+		if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) {
+			throw Error{Error::System, "fcntl"};
+		}
+#endif
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	bool get(Socket<Address, Protocol> &sc) const
+	{
+#if defined(O_NONBLOCK) && !defined(_WIN32)
+		int flags = fcntl(sc.handle(), F_GETFL, 0);
+
+		if (flags < 0) {
+			throw Error{Error::System, "fcntl"};
+		}
+
+		return !(flags & O_NONBLOCK);
+#else
+		throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"};
+#endif
+	}
+};
+
+/**
+ * @class SockReuseAddress
+ * @brief Reuse address, must be used before calling Socket::bind
+ */
+class SockReuseAddress {
+public:
+	/**
+	 * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option.
+	 */
+	bool value{true};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR));
+	}
+};
+
+/**
+ * @class SockSendBuffer
+ * @brief Set or get the output buffer.
+ */
+class SockSendBuffer {
+public:
+	/**
+	 * Set to the buffer size.
+	 */
+	int value{2048};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_SNDBUF, value);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline int get(Socket<Address, Protocol> &sc) const
+	{
+		return sc.template get<int>(SOL_SOCKET, SO_SNDBUF);
+	}
+};
+
+/**
+ * @class SockReceiveBuffer
+ * @brief Set or get the input buffer.
+ */
+class SockReceiveBuffer {
+public:
+	/**
+	 * Set to the buffer size.
+	 */
+	int value{2048};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_RCVBUF, value);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline int get(Socket<Address, Protocol> &sc) const
+	{
+		return sc.template get<int>(SOL_SOCKET, SO_RCVBUF);
+	}
+};
+
+/* }}} */
+
+/**
+ * @class TcpNoDelay
+ * @brief Set this option if you want to disable nagle's algorithm.
+ */
+class TcpNoDelay {
+public:
+	/**
+	 * Set to true to set TCP_NODELAY option.
+	 */
+	bool value{true};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY));
+	}
+};
+
+/**
+ * @class Ipv6Only
+ * @brief Control IPPROTO_IPV6/IPV6_V6ONLY
+ *
+ * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either
+ * false or true if portability is a concern.
+ */
+class Ipv6Only {
+public:
+	/**
+	 * Set this to use only IPv6.
+	 */
+	bool value{true};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY));
+	}
+};
+
+} // !option
+
+/* }}} */
+
+/*
+ * Predefined addressed to be used
+ * ------------------------------------------------------------------
+ *
+ * - Ip,
+ * - Local.
+ */
+
+/* {{{ Addresses */
+
+/**
+ * Set of predefined addresses.
+ */
+namespace address {
+
+/**
+ * @class Ip
+ * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4.
+ */
+class Ip {
+public:
+	/**
+	 * @enum Type
+	 * @brief Type of ip address.
+	 */
+	enum Type {
+		v4 = AF_INET,		//!< AF_INET
+		v6 = AF_INET6		//!< AF_INET6
+	};
+
+private:
+	/*
+	 * Default domain when using default constructors.
+	 *
+	 * Note: AF_INET or AF_INET6, not
+	 */
+	static int m_default;
+
+	union {
+		sockaddr_in m_sin;
+		sockaddr_in6 m_sin6;
+	};
+
+	socklen_t m_length{0};
+	int m_domain{AF_INET};
+
+public:
+	/**
+	 * Set the default domain to use when using default Ip constructor. By default, AF_INET is used.
+	 *
+	 * @pre domain must be Type::v4 or Type::v6
+	 */
+	static inline void setDefault(Type domain) noexcept
+	{
+		assert(domain == Type::v4 || domain == Type::v6);
+
+		m_default = static_cast<int>(domain);
+	}
+
+	/**
+	 * Construct using the default domain.
+	 */
+	inline Ip() noexcept
+		: Ip(static_cast<Type>(m_default))
+	{
+	}
+
+	/**
+	 * Default initialize the Ip domain.
+	 *
+	 * @pre domain must be AF_INET or AF_INET6 only
+	 * @param domain the domain (AF_INET or AF_INET6)
+	 */
+	Ip(Type domain) noexcept;
+
+	/**
+	 * Construct an address suitable for bind() or connect().
+	 *
+	 * @pre domain must be Type::v4 or Type::v6
+	 * @param domain the domain (AF_INET or AF_INET6)
+	 * @param host the host (* for any)
+	 * @param port the port number
+	 * @throw Error on errors
+	 */
+	Ip(const std::string &host, int port, Type domain = v4);
+
+	/**
+	 * Construct an address from a storage.
+	 *
+	 * @pre storage's domain must be AF_INET or AF_INET6 only
+	 * @param ss the storage
+	 * @param length the length
+	 */
+	Ip(const sockaddr_storage *ss, socklen_t length) noexcept;
+
+	/**
+	 * Get the domain (AF_INET or AF_INET6).
+	 *
+	 * @return the domain
+	 */
+	inline int domain() const noexcept
+	{
+		return m_domain;
+	}
+
+	/**
+	 * Return the underlying address, either sockaddr_in6 or sockaddr_in.
+	 *
+	 * @return the address
+	 */
+	inline const sockaddr *address() const noexcept
+	{
+		if (m_domain == AF_INET6) {
+			return reinterpret_cast<const sockaddr *>(&m_sin6);
+		}
+
+		return reinterpret_cast<const sockaddr *>(&m_sin);
+	}
+
+	/**
+	 * Return the underlying address length.
+	 *
+	 * @return the length
+	 */
+	inline socklen_t length() const noexcept
+	{
+		return m_length;
+	}
+
+	/**
+	 * Get the port.
+	 *
+	 * @return the port
+	 */
+	inline int port() const noexcept
+	{
+		if (m_domain == AF_INET6) {
+			return ntohs(m_sin6.sin6_port);
+		}
+
+		return ntohs(m_sin.sin_port);
+	}
+};
+
+#if !defined(_WIN32)
+
+/**
+ * @class Local
+ * @brief unix family sockets
+ *
+ * Create an address to a specific path. Only available on Unix.
+ */
+class Local {
+private:
+	sockaddr_un m_sun;
+	std::string m_path;
+
+public:
+	/**
+	 * Get the domain AF_LOCAL.
+	 *
+	 * @return AF_LOCAL
+	 */
+	inline int domain() const noexcept
+	{
+		return AF_LOCAL;
+	}
+
+	/**
+	 * Default constructor.
+	 */
+	Local() noexcept;
+
+	/**
+	 * Construct an address to a path.
+	 *
+	 * @param path the path
+	 * @param rm remove the file before (default: false)
+	 */
+	Local(std::string path, bool rm = false) noexcept;
+
+	/**
+	 * Construct an unix address from a storage address.
+	 *
+	 * @pre storage's domain must be AF_LOCAL
+	 * @param ss the storage
+	 * @param length the length
+	 */
+	Local(const sockaddr_storage *ss, socklen_t length) noexcept;
+
+	/**
+	 * Get the sockaddr_un.
+	 *
+	 * @return the address
+	 */
+	inline const sockaddr *address() const noexcept
+	{
+		return reinterpret_cast<const sockaddr *>(&m_sun);
+	}
+
+	/**
+	 * Get the address length.
+	 *
+	 * @return the length
+	 */
+	inline socklen_t length() const noexcept
+	{
+#if defined(SOCKET_HAVE_SUN_LEN)
+		return SUN_LEN(&m_sun);
+#else
+		return sizeof (m_sun);
+#endif
+	}
+};
+
+#endif // !_WIN32
+
+} // !address
+
+/* }}} */
+
+/*
+ * Predefined protocols
+ * ------------------------------------------------------------------
+ *
+ * - Tcp, for standard stream connections,
+ * - Udp, for standard datagram connections,
+ * - Tls, for secure stream connections.
+ */
+
+/* {{{ Protocols */
+
+/**
+ * Set of predefined protocols.
+ */
+namespace protocol {
+
+/* {{{ Tcp */
+
+/**
+ * @class Tcp
+ * @brief Clear TCP implementation.
+ *
+ * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual
+ * C functions.
+ */
+class Tcp {
+public:
+	/**
+	 * Socket type.
+	 *
+	 * @return SOCK_STREAM
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_STREAM;
+	}
+
+	/**
+	 * Do nothing.
+	 *
+	 * This function is just present for compatibility, it should never be called.
+	 */
+	template <typename Address>
+	inline void create(Socket<Address, Tcp> &) const noexcept
+	{
+		/* No-op */
+	}
+
+	/**
+	 * Standard connect.
+	 *
+	 * If the socket is marked non-blocking and the connection cannot be established immediately, then the
+	 * following is true:
+	 *
+	 * - state is set to State::Connecting,
+	 * - action is set to Action::Connect,
+	 * - condition is set to Condition::Writable.
+	 *
+	 * Then the user must wait until the socket is writable and call connect() with 0 arguments.
+	 *
+	 * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in
+	 * that case state is either set to State::Connected or State::Disconnected but action and condition are
+	 * not set.
+	 *
+	 * @param sc the socket
+	 * @param address the address
+	 * @param length the length
+	 * @throw net::Error on errors
+	 * @note Wrapper of connect(2)
+	 */
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length)
+	{
+		if (::connect(sc.handle(), address, length) == Failure) {
+			/*
+			 * Determine if the error comes from a non-blocking connect that cannot be
+			 * accomplished yet.
+			 */
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				sc.setState(State::Connecting);
+				sc.setAction(Action::Connect);
+				sc.setCondition(Condition::Writable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "connect", error};
+			}
+#else
+			if (errno == EINPROGRESS) {
+				sc.setState(State::Connecting);
+				sc.setAction(Action::Connect);
+				sc.setCondition(Condition::Writable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "connect"};
+			}
+#endif
+		} else {
+			sc.setState(State::Connected);
+		}
+	}
+
+	/**
+	 * Continue the connection. This function must only be called when the socket is ready for writing,
+	 * the user is responsible of waiting for that condition.
+	 *
+	 * This function check for SOL_SOCKET/SO_ERROR status.
+	 *
+	 * If the connection is complete, status is set to State::Connected, otherwise it is set to
+	 * State::Disconnected. In both cases, action and condition are not set.
+	 *
+	 * @param sc the socket
+	 * @throw net::Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc)
+	{
+		int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
+
+		if (error == Failure) {
+			sc.setState(State::Disconnected);
+			throw Error{Error::System, "connect", error};
+		}
+
+		sc.setState(State::Connected);
+	}
+
+	/**
+	 * Accept a clear client.
+	 *
+	 * If the socket is marked non-blocking and there are no pending connection, this function throws an
+	 * error. The user must wait that the socket is readable before calling this function.
+	 *
+	 * If the socket is blocking, this function blocks until a new client is connected or throws an error on
+	 * errors.
+	 *
+	 * If the socket is correctly returned, its state is set to State::Accepted and its action and condition
+	 * are not set.
+	 *
+	 * In any case, action and condition of this socket are not set.
+	 *
+	 * @param sc the socket
+	 * @param address the address destination
+	 * @param length the address length
+	 * @return the socket
+	 * @throw net::Error on errors
+	 * @note Wrapper of accept(2)
+	 */
+	template <typename Address, typename Protocol>
+	Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length)
+	{
+		Handle handle = ::accept(sc.handle(), address, length);
+
+		if (handle == Invalid) {
+			throw Error{Error::System, "accept"};
+		}
+
+		return Socket<Address, Protocol>{handle, State::Accepted};
+	}
+
+	/**
+	 * Continue accept.
+	 *
+	 * This function is just present for compatibility, it should never be called.
+	 */
+	template <typename Address, typename Protocol>
+	inline void accept(Socket<Address, Protocol> &) const noexcept
+	{
+		/* no-op */
+	}
+
+	/**
+	 * Receive data.
+	 *
+	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
+	 * Condition::Readable. If 0 is returned and condition is not set, then the state is set to
+	 * State::Disconnected.
+	 *
+	 * If the socket is blocking, this function blocks until some data is available or if an error occurs.
+	 *
+	 * In any case, action is never set.
+	 *
+	 * @param sc the socket
+	 * @param data the destination
+	 * @param length the destination length
+	 * @return the number of bytes read
+	 * @throw Error on errors
+	 * @note Wrapper of recv(2)
+	 */
+	template <typename Address>
+	unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length)
+	{
+		int nbread = ::recv(sc.handle(), (Arg)data, length, 0);
+
+		if (nbread == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbread = 0;
+				sc.setCondition(Condition::Readable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "recv", error};
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				sc.setCondition(Condition::Readable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "recv"};
+			}
+#endif
+		} else if (nbread == 0) {
+			sc.setState(State::Disconnected);
+		}
+
+		return static_cast<unsigned>(nbread);
+	}
+
+	/**
+	 * Send some data.
+	 *
+	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
+	 * Condition::Writable.
+	 *
+	 * If the socket is blocking, this function blocks until the data has been sent.
+	 *
+	 * On any other errors, this function throw net::Error.
+	 *
+	 * @param sc the socket
+	 * @param data the buffer to send
+	 * @param length the buffer length
+	 * @return the number of bytes sent
+	 * @throw net::Error on errors
+	 * @note Wrapper of send(2)
+	 */
+	template <typename Address>
+	unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length)
+	{
+		int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0);
+
+		if (nbsent == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbsent = 0;
+				sc.setCondition(Condition::Writable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "send", error};
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbsent = 0;
+				sc.setCondition(Condition::Writable);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "send"};
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbsent);
+	}
+};
+
+/* }}} */
+
+/* {{{ Udp */
+
+/**
+ * @class Udp
+ * @brief Clear UDP type.
+ *
+ * This class is the basic implementation of UDP sockets.
+ */
+class Udp {
+public:
+	/**
+	 * Socket type.
+	 *
+	 * @return SOCK_DGRAM
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_DGRAM;
+	}
+
+	/**
+	 * Do nothing.
+	 */
+	template <typename Address>
+	inline void create(Socket<Address, Udp> &) noexcept
+	{
+		/* No-op */
+	}
+
+	/**
+	 * Receive data from an end point.
+	 *
+	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
+	 * Condition::Readable.
+	 *
+	 * If the socket is blocking, this functions blocks until some data is available or if an error occurs.
+	 *
+	 * @param sc the socket
+	 * @param data the destination buffer
+	 * @param length the buffer length
+	 * @param address the address
+	 * @param addrlen the initial address length
+	 * @return the number of bytes received
+	 * @throw Error on error
+	 */
+	template <typename Address>
+	unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+	{
+		int nbread;
+
+		nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen);
+
+		if (nbread == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbread = 0;
+				sc.setCondition(Condition::Readable);
+			} else {
+				throw Error{Error::System, "recvfrom"};
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbread = 0;
+				sc.setCondition(Condition::Readable);
+			} else {
+				throw Error{Error::System, "recvfrom"};
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbread);
+	}
+
+	/**
+	 * Send data to an end point.
+	 *
+	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
+	 * Condition::Writable.
+	 *
+	 * If the socket is blocking, this functions blocks until the data has been sent.
+	 *
+	 * @param sc the socket
+	 * @param data the buffer
+	 * @param length the buffer length
+	 * @param address the client address
+	 * @param addrlen the adderss length
+	 * @return the number of bytes sent
+	 * @throw Error on error
+	 */
+	template <typename Address>
+	unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+	{
+		int nbsent;
+
+		nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen);
+		if (nbsent == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbsent = 0;
+				sc.setCondition(Condition::Writable);
+			} else {
+				throw Error{Error::System, "sendto", error};
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbsent = 0;
+				sc.setCondition(Condition::Writable);
+			} else {
+				throw Error{Error::System, "sendto"};
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbsent);
+	}
+};
+
+/* }}} */
+
+/* {{{ Tls */
+
+#if !defined(SOCKET_NO_SSL)
+
+/**
+ * @class Tls
+ * @brief OpenSSL secure layer for TCP.
+ *
+ * **Note:** This protocol is much more difficult to use with non-blocking sockets, if some operations would block, the
+ * user is responsible of calling the function again by waiting for the appropriate condition. See the functions for
+ * more details.
+ *
+ * @see Tls::accept
+ * @see Tls::connect
+ * @see Tls::recv
+ * @see Tls::send
+ */
+class Tls : private Tcp {
+private:
+	using Context = std::shared_ptr<SSL_CTX>;
+	using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>;
+
+	/* OpenSSL objects */
+	Context m_context;
+	Ssl m_ssl{nullptr, nullptr};
+
+	/* Status */
+	bool m_tcpconnected{false};
+
+	/*
+	 * User definable parameters
+	 */
+	ssl::Method m_method{ssl::Tlsv1};
+	std::string m_key;
+	std::string m_certificate;
+	bool m_verify{false};
+
+	/*
+	 * Construct with a context and ssl, for Tls::accept.
+	 */
+	Tls(Context context, Ssl ssl)
+		: m_context{std::move(context)}
+		, m_ssl{std::move(ssl)}
+	{
+	}
+
+	/*
+	 * Get the OpenSSL error message.
+	 */
+	inline std::string error(int error)
+	{
+		auto msg = ERR_reason_error_string(error);
+
+		return msg == nullptr ? "" : msg;
+	}
+
+	/*
+	 * Update the states after an uncompleted operation.
+	 */
+	template <typename Address, typename Protocol>
+	inline void updateStates(Socket<Address, Protocol> &sc, State state, Action action, int code)
+	{
+		assert(code == SSL_ERROR_WANT_READ || code == SSL_ERROR_WANT_WRITE);
+
+		sc.setState(state);
+		sc.setAction(action);
+
+		if (code == SSL_ERROR_WANT_READ) {
+			sc.setCondition(Condition::Readable);
+		} else {
+			sc.setCondition(Condition::Writable);
+		}
+	}
+
+	/*
+	 * Continue the connect operation.
+	 */
+	template <typename Address, typename Protocol>
+	void processConnect(Socket<Address, Protocol> &sc)
+	{
+		int ret = SSL_connect(m_ssl.get());
+
+		if (ret <= 0) {
+			int no = SSL_get_error(m_ssl.get(), ret);
+
+			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
+				updateStates(sc, State::Connecting, Action::Connect, no);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error{Error::System, "connect", error(no)};
+			}
+		} else {
+			sc.setState(State::Connected);
+		}
+	}
+
+	/*
+	 * Continue accept.
+	 */
+	template <typename Address, typename Protocol>
+	void processAccept(Socket<Address, Protocol> &sc)
+	{
+		int ret = SSL_accept(m_ssl.get());
+
+		if (ret <= 0) {
+			int no = SSL_get_error(m_ssl.get(), ret);
+
+			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
+				updateStates(sc, State::Accepting, Action::Accept, no);
+			} else {
+				sc.setState(State::Disconnected);
+				throw Error(Error::System, "accept", error(no));
+			}
+		} else {
+			sc.setState(State::Accepted);
+		}
+	}
+
+public:
+	/**
+	 * @copydoc Tcp::type
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_STREAM;
+	}
+
+	/**
+	 * Empty TLS constructor.
+	 */
+	Tls()
+	{
+#if !defined(SOCKET_NO_SSL_AUTO_INIT)
+		net::ssl::init();
+#endif
+	}
+
+	/**
+	 * Set the method.
+	 *
+	 * @param method the method
+	 * @pre the socket must not be already created
+	 */
+	inline void setMethod(ssl::Method method) noexcept
+	{
+		assert(!m_context);
+		assert(!m_ssl);
+
+		m_method = method;
+	}
+
+	/**
+	 * Use the specified private key file.
+	 *
+	 * @param file the path to the private key
+	 */
+	inline void setPrivateKey(std::string file) noexcept
+	{
+		m_key = std::move(file);
+	}
+
+	/**
+	 * Use the specified certificate file.
+	 *
+	 * @param file the path to the file
+	 */
+	inline void setCertificate(std::string file) noexcept
+	{
+		m_certificate = std::move(file);
+	}
+
+	/**
+	 * Set to true if we must verify the certificate and private key.
+	 *
+	 * @param verify the mode
+	 */
+	inline void setVerify(bool verify = true) noexcept
+	{
+		m_verify = verify;
+	}
+
+	/**
+	 * Initialize the SSL objects after have created.
+	 *
+	 * @param sc the socket
+	 * @throw net::Error on errors
+	 */
+	template <typename Address>
+	inline void create(Socket<Address, Tls> &sc)
+	{
+		auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv23_method();
+
+		m_context = {SSL_CTX_new(method), SSL_CTX_free};
+		m_ssl = {SSL_new(m_context.get()), SSL_free};
+
+		SSL_set_fd(m_ssl.get(), sc.handle());
+
+		/* Load certificates */
+		if (m_certificate.size() > 0) {
+			SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM);
+		}
+		if (m_key.size() > 0) {
+			SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM);
+		}
+		if (m_verify && !SSL_CTX_check_private_key(m_context.get())) {
+			throw Error{Error::System, "(openssl)", "unable to verify key"};
+		}
+	}
+
+	/**
+	 * Connect to a secure host.
+	 *
+	 * If the socket is marked non-blocking and the connection cannot be established yet, then the state is set
+	 * to State::Connecting, the condition is set to Condition::Readable or Condition::Writable, the user must
+	 * wait for the appropriate condition before calling the overload connect which takes 0 argument.
+	 *
+	 * If the socket is blocking, this functions blocks until the connection is complete.
+	 *
+	 * If the connection was completed correctly the state is set to State::Connected.
+	 *
+	 * @param sc the socket
+	 * @param address the address
+	 * @param length the address length
+	 * @throw net::Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length)
+	{
+		/* 1. Connect using raw TCP */
+		Tcp::connect(sc, address, length);
+
+		/* 2. If the connection is complete (e.g. non-blocking), try handshake */
+		if (sc.state() == State::Connected) {
+			m_tcpconnected = true;
+			processConnect(sc);
+		}
+	}
+
+	/**
+	 * Continue the connection.
+	 *
+	 * This function must be called when the socket is ready for reading or writing (check with Socket::condition),
+	 * the state may change exactly like the initial connect call.
+	 *
+	 * @param sc the socket
+	 * @throw net::Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc)
+	{
+		/* 1. Be sure to complete standard connect before */
+		if (!m_tcpconnected) {
+			Tcp::connect(sc);
+			m_tcpconnected = sc.state() == State::Connected;
+		}
+
+		if (m_tcpconnected) {
+			processConnect(sc);
+		}
+	}
+
+	/**
+	 * Accept a secure client.
+	 *
+	 * Because SSL needs several round-trips, if the socket is marked non-blocking and the connection is not
+	 * completed yet, a new socket is returned but with the State::Accepting state. Its condition is set to
+	 * Condition::Readable or Condition::Writable, the user is responsible of calling accept overload which takes
+	 * 0 arguments on the returned socket when the condition is met.
+	 *
+	 * If the socket is blocking, this function blocks until the client is accepted and returned.
+	 *
+	 * If the client is accepted correctly, its state is set to State::Accepted. This instance does not change.
+	 *
+	 * @param sc the socket
+	 * @param address the address destination
+	 * @param length the address length
+	 * @return the client
+	 * @throw net::Error on errors
+	 */
+	template <typename Address>
+	Socket<Address, Tls> accept(Socket<Address, Tls> &sc, sockaddr *address, socklen_t *length)
+	{
+		Socket<Address, Tls> client = Tcp::accept(sc, address, length);
+		Tls &proto = client.protocol();
+
+		/* 1. Share the context */
+		proto.m_context = m_context;
+
+		/* 2. Create new SSL instance */
+		proto.m_ssl = Ssl{SSL_new(m_context.get()), SSL_free};
+		SSL_set_fd(proto.m_ssl.get(), client.handle());
+
+		/* 3. Try accept process on the **new** client */
+		proto.processAccept(client);
+
+		return client;
+	}
+
+	/**
+	 * Continue accept.
+	 *
+	 * This function must be called on the client that is being accepted.
+	 *
+	 * Like accept or connect, user is responsible of calling this function until the connection is complete.
+	 *
+	 * @param sc the socket
+	 * @throw net::Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void accept(Socket<Address, Protocol> &sc)
+	{
+		processAccept(sc);
+	}
+
+	/**
+	 * Receive some secure data.
+	 *
+	 * If the socket is marked non-blocking, 0 is returned if no data is available yet or if the connection
+	 * needs renegociation. If renegociation is required case, the action is set to Action::Receive and condition
+	 * is set to Condition::Readable or Condition::Writable. The user must wait that the condition is met and
+	 * call this function again.
+	 *
+	 * @param sc the socket
+	 * @param data the destination
+	 * @param len the buffer length
+	 * @return the number of bytes read
+	 * @throw net::Error on errors
+	 */
+	template <typename Address>
+	unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len)
+	{
+		auto nbread = SSL_read(m_ssl.get(), data, len);
+
+		if (nbread <= 0) {
+			auto no = SSL_get_error(m_ssl.get(), nbread);
+
+			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
+				nbread = 0;
+				updateStates(sc, sc.state(), Action::Receive, no);
+			} else {
+				throw Error{Error::System, "recv", error(no)};
+			}
+		}
+
+		return nbread;
+	}
+
+	/**
+	 * Send some data.
+	 *
+	 * Like recv, if the socket is marked non-blocking and no data can be sent or a negociation is required,
+	 * condition and action are set. See receive for more details
+	 *
+	 * @param sc the socket
+	 * @param data the data to send
+	 * @param len the buffer length
+	 * @return the number of bytes sent
+	 * @throw net::Error on errors
+	 */
+	template <typename Address>
+	unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len)
+	{
+		auto nbsent = SSL_write(m_ssl.get(), data, len);
+
+		if (nbsent <= 0) {
+			auto no = SSL_get_error(m_ssl.get(), nbsent);
+
+			if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) {
+				nbsent = 0;
+				updateStates(sc, sc.state(), Action::Send, no);
+			} else {
+				throw Error{Error::System, "send", error(no)};
+			}
+		}
+
+		return nbsent;
+	}
+};
+
+#endif // !SOCKET_NO_SSL
+
+/* }}} */
+
+} // !protocol
+
+/* }}} */
+
+/*
+ * Convenient helpers
+ * ------------------------------------------------------------------
+ *
+ * - SocketTcp<Address>, for TCP sockets,
+ * - SocketUdp<Address>, for UDP sockets,
+ * - SocketTls<Address>, for secure TCP sockets.
+ */
+
+/* {{{ Helpers */
+
+/**
+ * Helper to create TCP sockets.
+ */
+template <typename Address>
+using SocketTcp = Socket<Address, protocol::Tcp>;
+
+/**
+ * Helper to create TCP/IP sockets.
+ */
+using SocketTcpIp = Socket<address::Ip, protocol::Tcp>;
+
+#if !defined(_WIN32)
+
+/**
+ * Helper to create TCP/Local sockets.
+ */
+using SocketTcpLocal = Socket<address::Local, protocol::Tcp>;
+
+#endif
+
+/**
+ * Helper to create UDP sockets.
+ */
+template <typename Address>
+using SocketUdp = Socket<Address, protocol::Udp>;
+
+/**
+ * Helper to create UDP/IP sockets.
+ */
+using SocketUdpIp = Socket<address::Ip, protocol::Udp>;
+
+#if !defined(SOCKET_NO_SSL)
+
+/**
+ * Helper to create OpenSSL TCP sockets.
+ */
+template <typename Address>
+using SocketTls = Socket<Address, protocol::Tls>;
+
+/**
+ * Helper to create OpenSSL TCP/Ip sockets.
+ */
+using SocketTlsIp = Socket<address::Ip, protocol::Tls>;
+
+#endif // !SOCKET_NO_SSL
+
+/* }}} */
+
+/*
+ * Select wrapper
+ * ------------------------------------------------------------------
+ *
+ * Wrapper for select(2) and other various implementations.
+ */
+
+/* {{{ Listener */
+
+/**
+ * @class ListenerStatus
+ * @brief Result of polling
+ *
+ * Result of a select call, returns the first ready socket found with its
+ * flags.
+ */
+class ListenerStatus {
+public:
+	Handle socket;		//!< which socket is ready
+	Condition flags;	//!< the flags
+};
+
+/**
+ * Table used in the socket listener to store which sockets have been
+ * set in which directions.
+ */
+using ListenerTable = std::map<Handle, Condition>;
+
+/**
+ * @class Select
+ * @brief Implements select(2)
+ *
+ * This class is the fallback of any other method, it is not preferred at all for many reasons.
+ */
+class Select {
+public:
+	/**
+	 * No-op, uses the ListenerTable directly.
+	 */
+	inline void set(const ListenerTable &, Handle, Condition, bool) noexcept {}
+
+	/**
+	 * No-op, uses the ListenerTable directly.
+	 */
+	inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept {}
+
+	/**
+	 * Return the sockets
+	 */
+	std::vector<ListenerStatus> wait(const ListenerTable &table, int ms);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "select";
+	}
+};
+
+#if defined(SOCKET_HAVE_POLL)
+
+/**
+ * @class Poll
+ * @brief Implements poll(2).
+ *
+ * Poll is widely supported and is better than select(2). It is still not the
+ * best option as selecting the sockets is O(n).
+ */
+class Poll {
+private:
+	std::vector<pollfd> m_fds;
+
+	short toPoll(Condition flags) const noexcept;
+	Condition toCondition(short &event) const noexcept;
+
+public:
+	/**
+	 * Set the handle.
+	 */
+	void set(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Unset the handle.
+	 */
+	void unset(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Wait for events.
+	 */
+	std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "poll";
+	}
+};
+
+#endif
+
+#if defined(SOCKET_HAVE_EPOLL)
+
+/**
+ * @class Epoll
+ * @brief Linux's epoll.
+ */
+class Epoll {
+private:
+	int m_handle;
+	std::vector<epoll_event> m_events;
+
+	Epoll(const Epoll &) = delete;
+	Epoll &operator=(const Epoll &) = delete;
+	Epoll(const Epoll &&) = delete;
+	Epoll &operator=(const Epoll &&) = delete;
+
+	uint32_t toEpoll(Condition flags) const noexcept;
+	Condition toCondition(uint32_t events) const noexcept;
+	void update(Handle sc, int op, int eflags);
+
+public:
+	/**
+	 * Construct the epoll instance.
+	 */
+	Epoll();
+
+	/**
+	 * Close the epoll instance.
+	 */
+	~Epoll();
+
+	/**
+	 * Set the handle.
+	 */
+	void set(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Unset the handle.
+	 */
+	void unset(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Wait for events.
+	 */
+	std::vector<ListenerStatus> wait(const ListenerTable &, int);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "epoll";
+	}
+};
+
+#endif
+
+#if defined(SOCKET_HAVE_KQUEUE)
+
+/**
+ * @class Kqueue
+ * @brief Implements kqueue(2).
+ *
+ * This implementation is available on all BSD and Mac OS X. It is better than
+ * poll(2) because it's O(1), however it's a bit more memory consuming.
+ */
+class Kqueue {
+private:
+	std::vector<struct kevent> m_result;
+	int m_handle;
+
+	Kqueue(const Kqueue &) = delete;
+	Kqueue &operator=(const Kqueue &) = delete;
+	Kqueue(Kqueue &&) = delete;
+	Kqueue &operator=(Kqueue &&) = delete;
+
+	void update(Handle sc, int filter, int kflags);
+
+public:
+	/**
+	 * Construct the kqueue instance.
+	 */
+	Kqueue();
+
+	/**
+	 * Destroy the kqueue instance.
+	 */
+	~Kqueue();
+
+	/**
+	 * Set the handle.
+	 */
+	void set(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Unset the handle.
+	 */
+	void unset(const ListenerTable &, Handle, Condition, bool);
+
+	/**
+	 * Wait for events.
+	 */
+	std::vector<ListenerStatus> wait(const ListenerTable &, int);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "kqueue";
+	}
+};
+
+#endif
+
+/**
+ * @class Listener
+ * @brief Synchronous multiplexing
+ *
+ * Convenient wrapper around the select() system call.
+ *
+ * This class is implemented using a bridge pattern to allow different uses
+ * of listener implementation.
+ *
+ * You should not reinstanciate a new Listener at each iteartion of your
+ * main loop as it can be extremely costly. Instead use the same listener that
+ * you can safely modify on the fly.
+ *
+ * Currently, poll, epoll, select and kqueue are available.
+ *
+ * To implement the backend, the following functions must be available:
+ *
+ * ### Set
+ *
+ * @code
+ * void set(const ListenerTable &, Handle sc, Condition condition, bool add);
+ * @endcode
+ *
+ * This function, takes the socket to be added and the flags. The condition is
+ * always guaranteed to be correct and the function will never be called twice
+ * even if the user tries to set the same flag again.
+ *
+ * An optional add argument is added for backends which needs to do different
+ * operation depending if the socket was already set before or if it is the
+ * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7).
+ *
+ * ### Unset
+ *
+ * @code
+ * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove);
+ * @endcode
+ *
+ * Like set, this function is only called if the condition is actually set and will
+ * not be called multiple times.
+ *
+ * Also like set, an optional remove argument is set if the socket is being
+ * completely removed (e.g no more flags are set for this socket).
+ *
+ * ### Wait
+ *
+ * @code
+ * std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
+ * @endcode
+ *
+ * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus,
+ * may throw any exceptions.
+ *
+ * ### Name
+ *
+ * @code
+ * inline const char *name() const noexcept
+ * @endcode
+ *
+ * Returns the backend name. Usually the class in lower case.
+ */
+template <typename Backend = SOCKET_DEFAULT_BACKEND>
+class Listener {
+private:
+	Backend m_backend;
+	ListenerTable m_table;
+
+public:
+	/**
+	 * Construct an empty listener.
+	 */
+	Listener() = default;
+
+	/**
+	 * Get the backend.
+	 *
+	 * @return the backend
+	 */
+	inline const Backend &backend() const noexcept
+	{
+		return m_backend;
+	}
+
+	/**
+	 * Get the non-modifiable table.
+	 *
+	 * @return the table
+	 */
+	inline const ListenerTable &table() const noexcept
+	{
+		return m_table;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator begin() const noexcept
+	{
+		return m_table.begin();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator cbegin() const noexcept
+	{
+		return m_table.cbegin();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator end() const noexcept
+	{
+		return m_table.end();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator cend() const noexcept
+	{
+		return m_table.cend();
+	}
+
+	/**
+	 * Add or update a socket to the listener.
+	 *
+	 * If the socket is already placed with the appropriate flags, the
+	 * function is a no-op.
+	 *
+	 * If incorrect flags are passed, the function does nothing.
+	 *
+	 * @param sc the socket
+	 * @param condition the condition (may be OR'ed)
+	 * @throw Error if the backend failed to set
+	 */
+	void set(Handle sc, Condition condition)
+	{
+		/* Invalid or useless flags */
+		if (condition == Condition::None || static_cast<int>(condition) > 0x3)
+			return;
+
+		auto it = m_table.find(sc);
+
+		/*
+		 * Do not update the table if the backend failed to add
+		 * or update.
+		 */
+		if (it == m_table.end()) {
+			m_backend.set(m_table, sc, condition, true);
+			m_table.emplace(sc, condition);
+		} else {
+			/* Remove flag if already present */
+			if ((condition & Condition::Readable) == Condition::Readable &&
+			    (it->second & Condition::Readable) == Condition::Readable) {
+				condition &= ~(Condition::Readable);
+			}
+			if ((condition & Condition::Writable) == Condition::Writable &&
+			    (it->second & Condition::Writable) == Condition::Writable) {
+				condition &= ~(Condition::Writable);
+			}
+
+			/* Still need a call? */
+			if (condition != Condition::None) {
+				m_backend.set(m_table, sc, condition, false);
+				it->second |= condition;
+			}
+		}
+	}
+
+	/**
+	 * Unset a socket from the listener, only the flags is removed
+	 * unless the two flagss are requested.
+	 *
+	 * For example, if you added a socket for both reading and writing,
+	 * unsetting the write flags will keep the socket for reading.
+	 *
+	 * @param sc the socket
+	 * @param condition the condition (may be OR'ed)
+	 * @see remove
+	 */
+	void unset(Handle sc, Condition condition)
+	{
+		auto it = m_table.find(sc);
+
+		/* Invalid or useless flags */
+		if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end())
+			return;
+
+		/*
+		 * Like set, do not update if the socket is already at the appropriate
+		 * state.
+		 */
+		if ((condition & Condition::Readable) == Condition::Readable &&
+		    (it->second & Condition::Readable) != Condition::Readable) {
+			condition &= ~(Condition::Readable);
+		}
+		if ((condition & Condition::Writable) == Condition::Writable &&
+		    (it->second & Condition::Writable) != Condition::Writable) {
+			condition &= ~(Condition::Writable);
+		}
+
+		if (condition != Condition::None) {
+			/* Determine if it's a complete removal */
+			bool removal = ((it->second) & ~(condition)) == Condition::None;
+
+			m_backend.unset(m_table, sc, condition, removal);
+
+			if (removal) {
+				m_table.erase(it);
+			} else {
+				it->second &= ~(condition);
+			}
+		}
+	}
+
+	/**
+	 * Remove completely the socket from the listener.
+	 *
+	 * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable);
+	 *
+	 * @param sc the socket
+	 */
+	inline void remove(Handle sc)
+	{
+		unset(sc, Condition::Readable | Condition::Writable);
+	}
+
+	/**
+	 * Remove all sockets.
+	 */
+	inline void clear()
+	{
+		while (!m_table.empty()) {
+			remove(m_table.begin()->first);
+		}
+	}
+
+	/**
+	 * Get the number of sockets in the listener.
+	 */
+	inline ListenerTable::size_type size() const noexcept
+	{
+		return m_table.size();
+	}
+
+	/**
+	 * Select a socket. Waits for a specific amount of time specified as the duration.
+	 *
+	 * @param duration the duration
+	 * @return the socket ready
+	 */
+	template <typename Rep, typename Ratio>
+	inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration)
+	{
+		assert(!m_table.empty());
+
+		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+
+		return m_backend.wait(m_table, cvt.count())[0];
+	}
+
+	/**
+	 * Overload with milliseconds.
+	 *
+	 * @param timeout the optional timeout in milliseconds
+	 * @return the socket ready
+	 */
+	inline ListenerStatus wait(int timeout = -1)
+	{
+		return wait(std::chrono::milliseconds(timeout));
+	}
+
+	/**
+	 * Select multiple sockets.
+	 *
+	 * @param duration the duration
+	 * @return the socket ready
+	 */
+	template <typename Rep, typename Ratio>
+	inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration)
+	{
+		assert(!m_table.empty());
+
+		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+
+		return m_backend.wait(m_table, cvt.count());
+	}
+
+	/**
+	 * Overload with milliseconds.
+	 *
+	 * @return the socket ready
+	 */
+	inline std::vector<ListenerStatus> waitMultiple(int timeout = -1)
+	{
+		return waitMultiple(std::chrono::milliseconds(timeout));
+	}
+};
+
+/* }}} */
+
+/*
+ * Callback
+ * ------------------------------------------------------------------
+ *
+ * Function owner with tests.
+ */
+
+/* {{{ Callback */
+
+/**
+ * @class Callback
+ * @brief Convenient signal owner that checks if the target is valid.
+ *
+ * This class also catch all errors thrown from signals to avoid interfering with our process.
+ */
+template <typename... Args>
+class Callback : public std::function<void (Args...)> {
+public:
+	/**
+	 * Inherited constructors.
+	 */
+	using std::function<void (Args...)>::function;
+
+	/**
+	 * Execute the callback only if a target is set.
+	 */
+	void operator()(Args... args) const
+	{
+		if (*this) {
+			try {
+				std::function<void (Args...)>::operator()(args...);
+			} catch (...) {
+			}
+		}
+	}
+};
+
+/* }}} */
+
+/*
+ * StreamConnection
+ * ------------------------------------------------------------------
+ *
+ * Client connected on the server side.
+ */
+
+/* {{{ StreamConnection */
+
+/**
+ * @class StreamConnection
+ * @brief Connected client on the server side.
+ *
+ * This object is created from StreamServer when a new client is connected, it is the higher
+ * level object of sockets and completely asynchronous.
+ */
+template <typename Address, typename Protocol>
+class StreamConnection {
+public:
+	/**
+	 * Called when the output has changed.
+	 */
+	using WriteHandler = Callback<>;
+
+private:
+	/* Signals */
+	WriteHandler m_onWrite;
+
+	/* Sockets and output buffer */
+	Socket<Address, Protocol> m_socket;
+	std::string m_output;
+
+public:
+	/**
+	 * Create the connection.
+	 *
+	 * @param s the socket
+	 */
+	StreamConnection(Socket<Address, Protocol> s)
+		: m_socket{std::move(s)}
+	{
+		m_socket.set(net::option::SockBlockMode{false});
+	}
+
+	/**
+	 * Access the underlying socket.
+	 *
+	 * @return the socket
+	 * @warning use with care
+	 */
+	inline Socket<Address, Protocol> &socket() noexcept
+	{
+		return m_socket;
+	}
+
+	/**
+	 * Access the current output.
+	 *
+	 * @return the output
+	 */
+	inline const std::string &output() const noexcept
+	{
+		return m_output;
+	}
+
+	/**
+	 * Overloaded function
+	 *
+	 * @return the output
+	 * @warning use with care, avoid modifying the output if you don't know what you're doing
+	 */
+	inline std::string &output() noexcept
+	{
+		return m_output;
+	}
+
+	/**
+	 * Post some data to be sent asynchronously.
+	 *
+	 * @param str the data to append
+	 */
+	inline void send(std::string str)
+	{
+		m_output += str;
+		m_onWrite();
+	}
+
+	/**
+	 * Kill the client.
+	 */
+	inline void close()
+	{
+		m_socket.close();
+	}
+
+	/**
+	 * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner
+	 * knows that there are some data to send.
+	 *
+	 * @param handler the handler
+	 * @warning you usually never need to set this yourself
+	 */
+	inline void setWriteHandler(WriteHandler handler)
+	{
+		m_onWrite = std::move(handler);
+	}
+};
+
+/* }}} */
+
+/*
+ * StreamServer
+ * ------------------------------------------------------------------
+ *
+ * Convenient stream oriented server.
+ */
+
+/* {{{ StreamServer */
+
+/**
+ * @class StreamServer
+ * @brief Convenient stream server for TCP and TLS.
+ *
+ * This class does all the things for you as accepting new clients, listening for it and sending data. It works
+ * asynchronously without blocking to let you control your process workflow.
+ *
+ * This class is not thread safe and you must not call any of the functions from different threads.
+ */
+template <typename Address, typename Protocol>
+class StreamServer {
+public:
+	/**
+	 * Handler when a new client is connected.
+	 */
+	using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
+
+	/**
+	 * Handler when a client is disconnected.
+	 */
+	using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
+
+	/**
+	 * Handler when data has been received from a client.
+	 */
+	using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
+
+	/**
+	 * Handler when data has been correctly sent to a client.
+	 */
+	using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
+
+	/**
+	 * Handler when an error occured.
+	 */
+	using ErrorHandler = Callback<const Error &>;
+
+	/**
+	 * Handler when there was a timeout.
+	 */
+	using TimeoutHandler = Callback<>;
+
+private:
+	using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>;
+
+	/* Signals */
+	ConnectionHandler m_onConnection;
+	DisconnectionHandler m_onDisconnection;
+	ReadHandler m_onRead;
+	WriteHandler m_onWrite;
+	ErrorHandler m_onError;
+	TimeoutHandler m_onTimeout;
+
+	/* Sockets */
+	Socket<Address, Protocol> m_master;
+	Listener<> m_listener;
+	ClientMap m_clients;
+
+	/*
+	 * Update flags depending on the required condition.
+	 */
+	void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
+	{
+		assert(client->socket().action() != Action::None);
+
+		m_listener.remove(client->socket().handle());
+		m_listener.set(client->socket().handle(), client->socket().condition());
+	}
+
+	/*
+	 * Continue accept process.
+	 */
+	template <typename AcceptCall>
+	void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc)
+	{
+		try {
+			/* Do the accept */
+			acceptFunc();
+
+			/* 1. First remove completely the client */
+			m_listener.remove(client->socket().handle());
+
+			/* 2. If accept is not finished, wait for the appropriate condition */
+			if (client->socket().state() == State::Accepted) {
+				/* 3. Client is accepted, notify the user */
+				m_listener.set(client->socket().handle(), Condition::Readable);
+				m_onConnection(client);
+			} else {
+				/* Operation still in progress */
+				updateFlags(client);
+			}
+		} catch (const Error &error) {
+			m_clients.erase(client->socket().handle());
+			m_listener.remove(client->socket().handle());
+			m_onError(error);
+		}
+	}
+
+	/*
+	 * Process initial accept of master socket, this is the initial accepting process. Except on errors, the
+	 * socket is stored but the user will be notified only once the socket is completely accepted.
+	 */
+	void processInitialAccept()
+	{
+		// TODO: store address too.
+		std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr));
+		std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client};
+
+		/* 1. Register output changed to update listener */
+		client->setWriteHandler([this, ptr] () {
+			auto client = ptr.lock();
+
+			/* Do not update the listener immediately if an action is pending */
+			if (client && client->socket().action() == Action::None && !client->output().empty()) {
+				m_listener.set(client->socket().handle(), Condition::Writable);
+			}
+		});
+
+		/* 2. Add the client */
+		m_clients.insert(std::make_pair(client->socket().handle(), client));
+
+		/*
+		 * 2. Do an initial check to set the listener flags, at this moment the socket may or not be
+		 *    completely accepted.
+		 */
+		processAccept(client, [&] () {});
+	}
+
+	/*
+	 * Read or complete the read operation.
+	 */
+	void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
+	{
+		/*
+		 * Read because there is something to read or because the pending operation is
+		 * read and must complete.
+		 */
+		auto buffer = client->socket().recv(512);
+
+		/*
+		 * Now the receive operation may be completed, in that case, two possibilities:
+		 *
+		 * 1. The action is set to None (completed)
+		 * 2. The action is still not complete, update the flags
+		 */
+		if (client->socket().action() == Action::None) {
+			/* Empty mean normal disconnection */
+			if (buffer.empty()) {
+				m_listener.remove(client->socket().handle());
+				m_clients.erase(client->socket().handle());
+				m_onDisconnection(client);
+			} else {
+				/*
+				 * At this step, it is possible that we were completing a receive operation, in this
+				 * case the write flag may be removed, add it if required.
+				 */
+				if (!client->output().empty()) {
+					m_listener.set(client->socket().handle(), Condition::Writable);
+				}
+
+				m_onRead(client, buffer);
+			}
+		} else {
+			/* Operation in progress */
+			updateFlags(client);
+		}
+	}
+
+	/*
+	 * Flush the output buffer.
+	 */
+	void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
+	{
+		auto &output = client->output();
+		auto nsent = client->socket().send(output);
+
+		if (client->socket().action() == Action::None) {
+			/* 1. Create a copy of content that has been sent */
+			auto sent = output.substr(0, nsent);
+
+			/* 2. Erase the content sent */
+			output.erase(0, nsent);
+
+			/* 3. Update listener */
+			if (output.empty()) {
+				m_listener.unset(client->socket().handle(), Condition::Writable);
+			}
+
+			/* 4. Notify user */
+			m_onWrite(client, sent);
+		} else {
+			updateFlags(client);
+		}
+	}
+
+	void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags)
+	{
+		try {
+			auto action = client->socket().action();
+
+			if (action == Action::Receive ||
+			    (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) {
+				processRead(client);
+			} else if ((flags & Condition::Writable) == Condition::Writable) {
+				processWrite(client);
+			}
+		} catch (const Error &error) {
+			m_onDisconnection(client);
+			m_listener.remove(client->socket().handle());
+			m_clients.erase(client->socket().handle());
+		}
+	}
+
+public:
+	/**
+	 * Create a stream server with the specified address to bind.
+	 *
+	 * @param protocol the protocol (Tcp or Tls)
+	 * @param address the address to bind
+	 * @param max the max number to listen
+	 * @throw Error on errors
+	 */
+	StreamServer(Protocol protocol, const Address &address, int max = 128)
+		: m_master{std::move(protocol), address}
+	{
+		// TODO: m_onError
+		m_master.set(SOL_SOCKET, SO_REUSEADDR, 1);
+		m_master.bind(address);
+		m_master.listen(max);
+		m_listener.set(m_master.handle(), Condition::Readable);
+	}
+
+	/**
+	 * Set the connection handler, called when a new client is connected.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setConnectionHandler(ConnectionHandler handler)
+	{
+		m_onConnection = std::move(handler);
+	}
+
+	/**
+	 * Set the disconnection handler, called when a client died.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setDisconnectionHandler(DisconnectionHandler handler)
+	{
+		m_onDisconnection = std::move(handler);
+	}
+
+	/**
+	 * Set the receive handler, called when a client has sent something.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setReadHandler(ReadHandler handler)
+	{
+		m_onRead = std::move(handler);
+	}
+
+	/**
+	 * Set the writing handler, called when some data has been sent to a client.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setWriteHandler(WriteHandler handler)
+	{
+		m_onWrite = std::move(handler);
+	}
+
+	/**
+	 * Set the error handler, called when unrecoverable error has occured.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setErrorHandler(ErrorHandler handler)
+	{
+		m_onError = std::move(handler);
+	}
+
+	/**
+	 * Set the timeout handler, called when the selection has timeout.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setTimeoutHandler(TimeoutHandler handler)
+	{
+		m_onTimeout = std::move(handler);
+	}
+
+	/**
+	 * Poll for the next event.
+	 *
+	 * @param timeout the timeout (-1 for indefinitely)
+	 * @throw Error on errors
+	 */
+	void poll(int timeout = -1)
+	{
+		try {
+			auto st = m_listener.wait(timeout);
+
+			if (st.socket == m_master.handle()) {
+				/* New client */
+				processInitialAccept();
+			} else {
+				/* Recv / Send / Accept on a client */
+				auto client = m_clients[st.socket];
+
+				if (client->socket().state() == State::Accepted) {
+					processSync(client, st.flags);
+				} else {
+					processAccept(client, [&] () { client->socket().accept(); });
+				}
+			}
+		} catch (const Error &error) {
+			if (error.code() == Error::Timeout) {
+				m_onTimeout();
+			} else {
+				m_onError(error);
+			}
+		}
+	}
+};
+
+/* }}} */
+
+/*
+ * StreamClient
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ StreamClient */
+
+/**
+ * @class StreamClient
+ * @brief Client side connection to a server.
+ *
+ * This class is not thread safe and you must not call any of the functions from different threads.
+ */
+template <typename Address, typename Protocol>
+class StreamClient {
+public:
+	/**
+	 * Handler when connection is complete.
+	 */
+	using ConnectionHandler = Callback<>;
+
+	/**
+	 * Handler when data has been received.
+	 */
+	using ReadHandler = Callback<const std::string &>;
+
+	/**
+	 * Handler when data has been sent correctly.
+	 */
+	using WriteHandler = Callback<const std::string &>;
+
+	/**
+	 * Handler when disconnected.
+	 */
+	using DisconnectionHandler = Callback<>;
+
+	/**
+	 * Handler on unrecoverable error.
+	 */
+	using ErrorHandler = Callback<const Error &>;
+
+	/**
+	 * Handler when timeout occured.
+	 */
+	using TimeoutHandler = Callback<>;
+
+private:
+	/* Signals */
+	ConnectionHandler m_onConnection;
+	ReadHandler m_onRead;
+	WriteHandler m_onWrite;
+	DisconnectionHandler m_onDisconnection;
+	ErrorHandler m_onError;
+	TimeoutHandler m_onTimeout;
+
+	/* Socket */
+	Socket<Address, Protocol> m_socket;
+	Listener<> m_listener;
+
+	/* Output buffer */
+	std::string m_output;
+
+	/*
+	 * Update the flags after an uncompleted operation. This function must only be called when the operation
+	 * has not complete (e.g. connect, recv, send).
+	 */
+	void updateFlags()
+	{
+		assert(m_socket.action() != Action::None);
+
+		m_listener.remove(m_socket.handle());
+		m_listener.set(m_socket.handle(), m_socket.condition());
+	}
+
+	/*
+	 * This is the generic connect helper, it will be used to both initiate the connection or to continue the
+	 * connection process if needed.
+	 *
+	 * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or
+	 * m_socket.connect().
+	 *
+	 * See poll() and connect() to understand.
+	 */
+	template <typename ConnectCall>
+	void processConnect(const ConnectCall &connectFunc)
+	{
+		/* Call m_socket.connect() or m_socket.connect(address) */
+		connectFunc();
+
+		/* Remove entirely */
+		m_listener.remove(m_socket.handle());
+
+		if (m_socket.state() == State::Connected) {
+			m_onConnection();
+			m_listener.set(m_socket.handle(), Condition::Readable);
+		} else {
+			/* Connection still in progress */
+			updateFlags();
+		}
+	}
+
+	/*
+	 * Receive or complete the receive command, if the command is not complete, the listener is updated
+	 * accordingly.
+	 */
+	void processRead()
+	{
+		auto received = m_socket.recv(512);
+
+		if (m_socket.action() == Action::None) {
+			/* 0 means disconnection */
+			if (received.empty()) {
+				m_onDisconnection();
+			} else {
+				/*
+				 * At this step, it is possible that we were completing a receive operation, in this
+				 * case the write flag may be removed, add it if required.
+				 */
+				if (m_output.empty()) {
+					m_listener.unset(m_socket.handle(), Condition::Writable);
+				}
+
+				m_onRead(received);
+			}
+		} else {
+			/* Receive operation in progress */
+			updateFlags();
+		}
+	}
+
+	/*
+	 * Send or complete the send command, if the command is not complete, the listener is updated
+	 * accordingly.
+	 */
+	void processWrite()
+	{
+		auto nsent = m_socket.send(m_output);
+
+		if (m_socket.action() == Action::None) {
+			/* 1. Make a copy of what has been sent */
+			auto sent = m_output.substr(0, nsent);
+
+			/* 2. Erase sent content */
+			m_output.erase(0, nsent);
+
+			/* 3. Update flags if needed */
+			if (m_output.empty()) {
+				m_listener.unset(m_socket.handle(), Condition::Writable);
+			}
+
+			/* 4. Notify user */
+			m_onWrite(sent);
+		} else {
+			/* Send operation in progress */
+			updateFlags();
+		}
+	}
+
+	/*
+	 * Receive or send.
+	 */
+	void processSync(Condition condition)
+	{
+		if ((m_socket.action() == Action::Receive) ||
+		    (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) {
+			processRead();
+		} else {
+			processWrite();
+		}
+	}
+
+public:
+	/**
+	 * Create a client. The client is automatically marked as non-blocking.
+	 *
+	 * @param protocol the protocol (Tcp or Tls)
+	 * @param address the optional address
+	 * @throw net::Error on failures
+	 */
+	StreamClient(Protocol protocol = {}, const Address &address = {})
+		: m_socket{std::move(protocol), address}
+	{
+		m_socket.set(net::option::SockBlockMode{false});
+		m_listener.set(m_socket.handle(), Condition::Readable);
+	}
+
+	/**
+	 * Set the connection handler, called when the connection succeed.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setConnectionHandler(ConnectionHandler handler)
+	{
+		m_onConnection = std::move(handler);
+	}
+
+	/**
+	 * Set the disconnection handler, called when the server closed the connection.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setDisconnectionHandler(DisconnectionHandler handler)
+	{
+		m_onDisconnection = std::move(handler);
+	}
+
+	/**
+	 * Set the read handler, called when we received something.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setReadHandler(ReadHandler handler)
+	{
+		m_onRead = std::move(handler);
+	}
+
+	/**
+	 * Set the write handler, called when we successfully sent data.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setWriteHandler(WriteHandler handler)
+	{
+		m_onWrite = std::move(handler);
+	}
+
+	/**
+	 * Set the error handler, called when unexpected error occurs.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setErrorHandler(ErrorHandler handler)
+	{
+		m_onError = std::move(handler);
+	}
+
+	/**
+	 * Connect to a server, this function may connect immediately or not in any case the connection handler
+	 * will be called when the connection completed.
+	 *
+	 * @param address the address to connect to
+	 */
+	void connect(const Address &address) noexcept
+	{
+		assert(m_socket.state() == State::Open);
+
+		processConnect([&] () { m_socket.connect(address); });
+	}
+
+	/**
+	 * Asynchronously send data to the server.
+	 *
+	 * @param str the data to append
+	 */
+	void send(std::string str)
+	{
+		m_output += str;
+
+		/* Don't update the listener if there is a pending operation */
+		if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) {
+			m_listener.set(m_socket.handle(), Condition::Writable);
+		}
+	}
+
+	/**
+	 * Wait for the next event.
+	 *
+	 * @param timeout the time to wait in milliseconds
+	 * @throw Error on errors
+	 */
+	void poll(int timeout = -1) noexcept
+	{
+		try {
+			auto st = m_listener.wait(timeout);
+
+			if (m_socket.state() != State::Connected) {
+				/* Continue the connection */
+				processConnect([&] () { m_socket.connect(); });
+			} else {
+				/* Read / Write */
+				processSync(st.flags);
+			}
+		} catch (const Error &error) {
+			if (error.code() == Error::Timeout) {
+				m_onTimeout();
+			} else {
+				m_listener.remove(m_socket.handle());
+				m_onError(error);
+			}
+		}
+	}
+};
+
+/* }}} */
+
+} // !net
+
+} // !irccd
+
+#endif // !_SOCKETS_H_
--- a/lib/irccd/system.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/system.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -67,8 +67,7 @@
 #  include <pwd.h>
 #endif
 
-#include "private/filesystem.h"
-
+#include "filesystem.h"
 #include "logger.h"
 #include "system.h"
 #include "util.h"
--- a/lib/irccd/timer.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/timer.h	Wed Mar 30 13:52:47 2016 +0200
@@ -30,7 +30,7 @@
 #include <mutex>
 #include <thread>
 
-#include <irccd/private/signals.h>
+#include "signals.h"
 
 namespace irccd {
 
@@ -124,7 +124,6 @@
 	/**
 	 * Stop the timer, may be used by the user to stop it.
 	 *
-	 * @pre isRunning() must return true
 	 * @note Thread-safe
 	 */
 	void stop();
--- a/lib/irccd/transport-client.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/transport-client.h	Wed Mar 30 13:52:47 2016 +0200
@@ -29,10 +29,9 @@
 #include <stdexcept>
 #include <string>
 
-#include <irccd/private/signals.h>
-#include <irccd/private/sockets.h>
-
 #include "server.h"
+#include "signals.h"
+#include "sockets.h"
 
 namespace irccd {
 
--- a/lib/irccd/transport-server.h	Sat Mar 26 14:41:53 2016 +0100
+++ b/lib/irccd/transport-server.h	Wed Mar 30 13:52:47 2016 +0200
@@ -28,8 +28,8 @@
 #include <string>
 
 #include <irccd-config.h>
-#include <irccd/private/sockets.h>
 
+#include "sockets.h"
 #include "transport-client.h"
 
 namespace irccd {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/xdg.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,129 @@
+/*
+ * Xdg.cpp -- XDG directory specifications
+ *
+ * Copyright (c) 2013, 2014, 2015 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 <cstdlib>
+#include <stdexcept>
+#include <sstream>
+
+#include "xdg.h"
+
+namespace irccd {
+
+namespace {
+
+bool isabsolute(const std::string &path)
+{
+	return path.length() > 0 && path[0] == '/';
+}
+
+std::vector<std::string> split(const std::string &arg)
+{
+	std::stringstream iss(arg);
+	std::string item;
+	std::vector<std::string> elems;
+
+	while (std::getline(iss, item, ':'))
+		if (isabsolute(item))
+			elems.push_back(item);
+
+	return elems;
+}
+
+std::string envOrHome(const std::string &var, const std::string &repl)
+{
+	auto value = getenv(var.c_str());
+
+	if (value == nullptr || !isabsolute(value)) {
+		auto home = getenv("HOME");
+
+		if (home == nullptr)
+			throw std::runtime_error("could not get home directory");
+
+		return std::string(home) + "/" + repl;
+	}
+
+	return value;
+}
+
+std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list)
+{
+	auto value = getenv(var.c_str());
+
+	if (!value)
+		return list;
+
+	// No valid item at all? Use defaults
+	auto result = split(value);
+
+	return (result.size() == 0) ? list : result;
+}
+
+} // !namespace
+
+Xdg::Xdg()
+{
+	m_configHome	= envOrHome("XDG_CONFIG_HOME", ".config");
+	m_dataHome	= envOrHome("XDG_DATA_HOME", ".local/share");
+	m_cacheHome	= envOrHome("XDG_CACHE_HOME", ".cache");
+
+	m_configDirs	= listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" });
+	m_dataDirs	= listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" });
+
+	/*
+	 * Runtime directory is a special case and does not have a replacement, the
+	 * application should manage this by itself.
+	 */
+	auto runtime = getenv("XDG_RUNTIME_DIR");
+	if (runtime && isabsolute(runtime))
+		m_runtimeDir = runtime;
+}
+
+const std::string &Xdg::configHome() const noexcept
+{
+	return m_configHome;
+}
+
+const std::string &Xdg::dataHome() const noexcept
+{
+	return m_dataHome;
+}
+
+const std::string &Xdg::cacheHome() const noexcept
+{
+	return m_cacheHome;
+}
+
+const std::string &Xdg::runtimeDir() const
+{
+	if (m_runtimeDir.size() == 0)
+		throw std::runtime_error("XDG_RUNTIME_DIR is not set");
+
+	return m_runtimeDir;
+}
+
+const Xdg::List &Xdg::configDirs() const noexcept
+{
+	return m_configDirs;
+}
+
+const Xdg::List &Xdg::dataDirs() const noexcept
+{
+	return m_dataDirs;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/xdg.h	Wed Mar 30 13:52:47 2016 +0200
@@ -0,0 +1,112 @@
+/*
+ * Xdg.h -- XDG directory specifications
+ *
+ * Copyright (c) 2013, 2014, 2015 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_XDG_H_
+#define _IRCCD_XDG_H_
+
+/**
+ * @file Xdg.h
+ * @brief Read XDG standard specifications
+ */
+
+#include <vector>
+#include <string>
+
+namespace irccd {
+
+/**
+ * @class Xdg
+ * @brief XDG specifications
+ *
+ * Read and get XDG directories. This file contains exports thingies so it can
+ * compiles successfully on Windows but its usage is discouraged.
+ */
+class Xdg {
+public:
+	/**
+	 * list of directories.
+	 */
+	using List	= std::vector<std::string>;
+
+private:
+	std::string	m_configHome;
+	std::string	m_dataHome;
+	std::string	m_cacheHome;
+	std::string	m_runtimeDir;
+	List		m_configDirs;
+	List		m_dataDirs;
+
+public:
+	/**
+	 * Open an xdg instance and load directories.
+	 *
+	 * @throw std::runtime_error on failures
+	 */
+	Xdg();
+
+	/**
+	 * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config
+	 *
+	 * @return the config directory
+	 */
+	const std::string &configHome() const noexcept;
+
+	/**
+	 * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share
+	 *
+	 * @return the data directory
+	 */
+	const std::string &dataHome() const noexcept;
+
+	/**
+	 * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache
+	 *
+	 * @return the cache directory
+	 */
+	const std::string &cacheHome() const noexcept;
+
+	/**
+	 * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set,
+	 * if not, it throws an exception.
+	 *
+	 * The XDG standard says that application should handle XDG_RUNTIME_DIR by
+	 * themselves.
+	 *
+	 * @return the runtime directory
+	 * @throw std::runtime_error on error
+	 */
+	const std::string &runtimeDir() const;
+
+	/**
+	 * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" }
+	 *
+	 * @return the list of config directories
+	 */
+	const List &configDirs() const noexcept;
+
+	/**
+	 * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" }
+	 *
+	 * @return the list of data directories
+	 */
+	const List &dataDirs() const noexcept;
+};
+
+} // !irccd
+
+#endif // !_IRCCD_XDG_H_
--- a/tests/elapsedtimer/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/elapsedtimer/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -19,5 +19,5 @@
 irccd_define_test(
 	NAME elapsedtimer
 	SOURCES main.cpp
-	LIBRARIES common
+	LIBRARIES libirccd
 )
--- a/tests/elapsedtimer/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/elapsedtimer/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,7 +20,7 @@
 
 #include <gtest/gtest.h>
 
-#include <elapsed-timer.h>
+#include <irccd/elapsed-timer.h>
 
 using namespace irccd;
 using namespace std::chrono_literals;
--- a/tests/js-elapsedtimer/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-elapsedtimer/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,14 +18,7 @@
 
 irccd_define_test(
 	NAME js-elapsedtimer
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-elapsed-timer.cpp
-		${irccd_SOURCE_DIR}/js-elapsed-timer.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
 
--- a/tests/js-elapsedtimer/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-elapsedtimer/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,8 +20,8 @@
 
 #include <thread>
 
-#include <js-irccd.h>
-#include <js-elapsed-timer.h>
+#include <irccd/js-irccd.h>
+#include <irccd/js-elapsed-timer.h>
 
 using namespace irccd;
 using namespace std::chrono_literals;
@@ -33,7 +33,7 @@
 
 class TestElapsedTimer : public testing::Test {
 protected:
-	js::Context m_context;
+	duk::Context m_context;
 
 	TestElapsedTimer()
 	{
@@ -50,62 +50,93 @@
 
 TEST_F(TestElapsedTimer, standard)
 {
-	m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"});
+	try {
+		if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0)
+			throw duk::error(m_context, -1);
+
+		std::this_thread::sleep_for(300ms);
 
-	std::this_thread::sleep_for(300ms);
+		if (duk::pevalString(m_context, "result = timer.elapsed();") != 0)
+			throw duk::error(m_context, -1);
 
-	m_context.peval(js::Script{"result = timer.elapsed();"});
-	assertRange(m_context.getGlobal<int>("result"), 300);
+		assertRange(duk::getGlobal<int>(m_context, "result"), 300);
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
 }
 
 TEST_F(TestElapsedTimer, reset)
 {
-	m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"});
+	try {
+		if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0)
+			throw duk::error(m_context, -1);
+
+		std::this_thread::sleep_for(300ms);
 
-	std::this_thread::sleep_for(300ms);
+		if (duk::pevalString(m_context, "timer.reset(); result = timer.elapsed();") != 0)
+			throw duk::error(m_context, -1);
 
-	m_context.peval(js::Script{"timer.reset(); result = timer.elapsed();"});
-	assertRange(m_context.getGlobal<int>("result"), 0);
+		assertRange(duk::getGlobal<int>(m_context, "result"), 0);
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
 }
 
 TEST_F(TestElapsedTimer, pause)
 {
-	m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"});
+	try {
+		if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0)
+			throw duk::error(m_context, -1);
+
+		/*
+		 * Simulate a pause in the game like this:
+		 *
+		 * start     pause restart elapsed
+		 * |   10ms   |.5ms.| 6ms  |
+		 *
+		 * Since the game was paused, the 5ms must not be totalized.
+		 */
+		std::this_thread::sleep_for(10ms);
 
-	/*
-	 * Simulate a pause in the game like this:
-	 *
-	 * start     pause restart elapsed
-	 * |   10ms   |.5ms.| 6ms  |
-	 *
-	 * Since the game was paused, the 5ms must not be totalized.
-	 */
-	std::this_thread::sleep_for(10ms);
+		if (duk::pevalString(m_context, "timer.pause();") != 0)
+			throw duk::error(m_context, -1);
+
+		std::this_thread::sleep_for(5ms);
+
+		if (duk::pevalString(m_context, "timer.restart();") != 0)
+			throw duk::error(m_context, -1);
 
-	m_context.peval(js::Script{"timer.pause();"});
+		std::this_thread::sleep_for(6ms);
 
-	std::this_thread::sleep_for(5ms);
+		if (duk::pevalString(m_context, "result = timer.elapsed()") != 0)
+			throw duk::error(m_context, -1);
 
-	m_context.peval(js::Script{"timer.restart();"});
-
-	std::this_thread::sleep_for(6ms);
-
-	m_context.peval(js::Script{"result = timer.elapsed()"});
-	assertRange(m_context.getGlobal<int>("result"), 16);
+		assertRange(duk::getGlobal<int>(m_context, "result"), 16);
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
 }
 
 TEST_F(TestElapsedTimer, doublecheck)
 {
-	m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"});
+	try {
+		if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0)
+			throw duk::error(m_context, -1);
 
-	std::this_thread::sleep_for(50ms);
+		std::this_thread::sleep_for(50ms);
+
+		if (duk::pevalString(m_context, "result = timer.elapsed()") != 0)
+			throw duk::error(m_context, -1);
 
-	m_context.peval(js::Script{"result = timer.elapsed()"});
+		std::this_thread::sleep_for(50ms);
+
+		if (duk::pevalString(m_context, "result = timer.elapsed()") != 0)
+			throw duk::error(m_context, -1);
 
-	std::this_thread::sleep_for(50ms);
-
-	m_context.peval(js::Script{"result = timer.elapsed()"});
-	assertRange(m_context.getGlobal<int>("result"), 100);
+		assertRange(duk::getGlobal<int>(m_context, "result"), 100);
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
 }
 
 int main(int argc, char **argv)
--- a/tests/js-file/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-file/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,15 +18,8 @@
 
 irccd_define_test(
 	NAME js-file
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js-file.cpp
-		${irccd_SOURCE_DIR}/js-file.h
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
 
 #
--- a/tests/js-file/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-file/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,22 +20,23 @@
 
 #include <gtest/gtest.h>
 
-#include <js-file.h>
-#include <js-irccd.h>
+#include <irccd/js-file.h>
+#include <irccd/js-irccd.h>
 
 using namespace irccd;
 
 TEST(TestJsFile, functionBasename)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.File.basename('/usr/local/etc/irccd.conf');"});
+		if (duk::pevalString(ctx, "result = Irccd.File.basename('/usr/local/etc/irccd.conf');") != 0)
+			throw duk::error(ctx, -1);
 
-		ASSERT_EQ("irccd.conf", ctx.getGlobal<std::string>("result"));
+		ASSERT_EQ("irccd.conf", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -43,15 +44,15 @@
 
 TEST(TestJsFile, functionDirname)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"});
+		duk::pevalString(ctx, "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');");
 
-		ASSERT_EQ("/usr/local/etc", ctx.getGlobal<std::string>("result"));
+		ASSERT_EQ("/usr/local/etc", duk::getGlobal<std::string>(ctx,"result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -59,16 +60,16 @@
 
 TEST(TestJsFile, functionExists)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{"result = Irccd.File.exists(directory + '/file.txt')"});
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+		duk::pevalString(ctx, "result = Irccd.File.exists(directory + '/file.txt')");
 
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx,"result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -76,15 +77,15 @@
 
 TEST(TestJsFile, functionExists2)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.File.exists('file_which_does_not_exist.txt')"});
+		duk::pevalString(ctx, "result = Irccd.File.exists('file_which_does_not_exist.txt')");
 
-		ASSERT_FALSE(ctx.getGlobal<bool>("result"));
+		ASSERT_FALSE(duk::getGlobal<bool>(ctx,"result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -97,13 +98,14 @@
 		std::ofstream out("test-js-fs.remove");
 	}
 
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.peval(js::Script{"Irccd.File.remove('test-js-fs.remove');"});
+		if (duk::pevalString(ctx, "Irccd.File.remove('test-js-fs.remove');") != 0)
+			throw duk::error(ctx, -1);
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -115,19 +117,23 @@
 
 TEST(TestJsFile, methodBasename)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');"
 			"result = f.basename();"
-		});
+		);
 
-		ASSERT_EQ("file-1.txt", ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(ctx,"result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -135,20 +141,24 @@
 
 TEST(TestJsFile, methodBasenameClosed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');"
 			"f.close();"
 			"result = f.basename();"
-		});
+		);
 
-		ASSERT_EQ("file-1.txt", ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(ctx,"result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -156,19 +166,23 @@
 
 TEST(TestJsFile, methodDirname)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');"
 			"result = f.dirname();"
-		});
+		);
 
-		ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -176,20 +190,24 @@
 
 TEST(TestJsFile, methodDirnameClosed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');"
 			"f.close();"
 			"result = f.dirname();"
-		});
+		);
 
-		ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -197,20 +215,24 @@
 
 TEST(TestJsFile, methodSeek1)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.seek(Irccd.File.SeekSet, 4);"
 			"result = f.read(1);"
-		});
+		);
 
-		ASSERT_EQ(".", ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ(".", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -219,22 +241,27 @@
 
 TEST(TestJsFile, methodSeek1Closed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.close();"
 			"f.seek(Irccd.File.SeekSet, 4);"
 			"result = f.read(1);"
 			"result = typeof (result) === \"undefined\";"
-		});
+		);
+
 
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -242,21 +269,26 @@
 
 TEST(TestJsFile, methodSeek2)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.seek(Irccd.File.SeekSet, 2);"
 			"f.seek(Irccd.File.SeekCur, 2);"
 			"result = f.read(1);"
-		});
+		);
+
 
-		ASSERT_EQ(".", ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ(".", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -264,23 +296,28 @@
 
 TEST(TestJsFile, methodSeek2Closed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.close();"
 			"f.seek(Irccd.File.SeekSet, 2);"
 			"f.seek(Irccd.File.SeekCur, 2);"
 			"result = f.read(1);"
 			"result = typeof (result) === \"undefined\";"
-		});
+		);
+
 
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -288,20 +325,25 @@
 
 TEST(TestJsFile, methodSeek3)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.seek(Irccd.File.SeekEnd, -2);"
 			"result = f.read(1);"
-		});
+		);
+
 
-		ASSERT_EQ("x", ctx.getGlobal<std::string>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ("x", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -309,22 +351,27 @@
 
 TEST(TestJsFile, methodSeek3Closed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"f = new Irccd.File(directory + '/file.txt', 'r');"
 			"f.close();"
 			"f.seek(Irccd.File.SeekEnd, -2);"
 			"result = f.read(1);"
 			"result = typeof (result) === \"undefined\";"
-		});
+		);
+
 
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -332,24 +379,28 @@
 
 TEST(TestJsFile, methodReadline)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"lines = [];"
 			"f = new Irccd.File(directory + '/lines.txt', 'r');"
 			"for (var s; s = f.readline(); ) {"
 			"  lines.push(s);"
 			"}"
-		});
+		);
+
+		if (ret != 0)
+			throw duk::error(ctx, -1);
 
 		std::vector<std::string> expected{"a", "b", "c"};
 
-		ASSERT_EQ(expected, ctx.getGlobal<std::vector<std::string>>("lines"));
+		ASSERT_EQ(expected, duk::getGlobal<std::vector<std::string>>(ctx, "lines"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -357,25 +408,29 @@
 
 TEST(TestJsFile, methodReadlineClosed)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsFile(ctx);
 
 	try {
-		ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY);
-		ctx.peval(js::Script{
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
 			"lines = [];"
 			"f = new Irccd.File(directory + '/lines.txt', 'r');"
 			"f.close();"
 			"for (var s; s = f.readline(); ) {"
 			"  lines.push(s);"
 			"}"
-		});
+		);
+
+		if (ret != 0)
+			throw duk::error(ctx, -1);
 
 		std::vector<std::string> expected;
 
-		ASSERT_EQ(expected, ctx.getGlobal<std::vector<std::string>>("lines"));
+		ASSERT_EQ(expected, duk::getGlobal<std::vector<std::string>>(ctx, "lines"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
--- a/tests/js-irccd/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-irccd/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,11 +18,6 @@
 
 irccd_define_test(
 	NAME js-irccd
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/js-irccd/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-irccd/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,28 +20,30 @@
 
 #include <irccd-config.h>
 
-#include <logger.h>
-
-#include <js-irccd.h>
+#include <irccd/js-irccd.h>
+#include <irccd/logger.h>
 
 using namespace irccd;
 
 TEST(TestJsIrccd, version)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 
 	try {
-		ctx.peval(js::Script{
+		auto ret = duk::pevalString(ctx,
 			"major = Irccd.version.major;"
 			"minor = Irccd.version.minor;"
 			"patch = Irccd.version.patch;"
-		});
+		);
 
-		ASSERT_EQ(IRCCD_VERSION_MAJOR, ctx.getGlobal<int>("major"));
-		ASSERT_EQ(IRCCD_VERSION_MINOR, ctx.getGlobal<int>("minor"));
-		ASSERT_EQ(IRCCD_VERSION_PATCH, ctx.getGlobal<int>("patch"));
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		ASSERT_EQ(IRCCD_VERSION_MAJOR, duk::getGlobal<int>(ctx, "major"));
+		ASSERT_EQ(IRCCD_VERSION_MINOR, duk::getGlobal<int>(ctx, "minor"));
+		ASSERT_EQ(IRCCD_VERSION_PATCH, duk::getGlobal<int>(ctx, "patch"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
--- a/tests/js-logger/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-logger/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,13 +18,6 @@
 
 irccd_define_test(
 	NAME js-logger
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-logger.cpp
-		${irccd_SOURCE_DIR}/js-logger.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/js-logger/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-logger/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,10 +20,9 @@
 
 #include <irccd-config.h>
 
-#include <logger.h>
-
-#include <js-irccd.h>
-#include <js-logger.h>
+#include <irccd/js-irccd.h>
+#include <irccd/js-logger.h>
+#include <irccd/logger.h>
 
 using namespace irccd;
 
@@ -57,14 +56,16 @@
 
 TEST(TestJsLogger, info)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsLogger(ctx);
 
 	try {
-		ctx.putGlobal("\xff""\xff""name", "test");
-		ctx.peval(js::Script{"Irccd.Logger.info(\"hello!\");"});
+		duk::putGlobal(ctx, "\xff""\xff""name", "test");
+
+		if (duk::pevalString(ctx, "Irccd.Logger.info(\"hello!\");") != 0)
+			throw duk::error(ctx, -1);
 
 		ASSERT_EQ("plugin test: hello!", lineInfo);
 	} catch (const std::exception &ex) {
@@ -74,14 +75,16 @@
 
 TEST(TestJsLogger, warning)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsLogger(ctx);
 
 	try {
-		ctx.putGlobal("\xff""\xff""name", "test");
-		ctx.peval(js::Script{"Irccd.Logger.warning(\"FAIL!\");"});
+		duk::putGlobal(ctx, "\xff""\xff""name", "test");
+
+		if (duk::pevalString(ctx, "Irccd.Logger.warning(\"FAIL!\");") != 0)
+			throw duk::error(ctx, -1);
 
 		ASSERT_EQ("plugin test: FAIL!", lineWarning);
 	} catch (const std::exception &ex) {
@@ -93,14 +96,16 @@
 
 TEST(TestJsLogger, debug)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsLogger(ctx);
 
 	try {
-		ctx.putGlobal("\xff""\xff""name", "test");
-		ctx.peval(js::Script{"Irccd.Logger.debug(\"starting\");"});
+		duk::putGlobal(ctx, "\xff""\xff""name", "test");
+
+		if (duk::pevalString(ctx, "Irccd.Logger.debug(\"starting\");") != 0)
+			throw duk::error(ctx, -1);
 
 		ASSERT_EQ("plugin test: starting", lineDebug);
 	} catch (const std::exception &ex) {
--- a/tests/js-system/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-system/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,16 +18,7 @@
 
 irccd_define_test(
 	NAME js-system
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.h
-		${irccd_SOURCE_DIR}/js-file.cpp
-		${irccd_SOURCE_DIR}/js-file.h
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-system.cpp
-		${irccd_SOURCE_DIR}/js-system.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
 
--- a/tests/js-system/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-system/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,24 +18,23 @@
 
 #include <gtest/gtest.h>
 
-#include <system.h>
-
-#include <js-irccd.h>
-#include <js-system.h>
+#include <irccd/js-irccd.h>
+#include <irccd/js-system.h>
+#include <irccd/system.h>
 
 using namespace irccd;
 
 TEST(TestJsSystem, home)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsSystem(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.System.home();"});
+		duk::pevalString(ctx, "result = Irccd.System.home();");
 
-		ASSERT_EQ(sys::home(), ctx.getGlobal<std::string>("result"));
+		ASSERT_EQ(sys::home(), duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
--- a/tests/js-timer/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-timer/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,49 +18,10 @@
 
 irccd_define_test(
 	NAME js-timer
-	SOURCES
-		${irccd_SOURCE_DIR}/irccd.cpp
-		${irccd_SOURCE_DIR}/irccd.h
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.h
-		${irccd_SOURCE_DIR}/js-directory.cpp
-		${irccd_SOURCE_DIR}/js-directory.h
-		${irccd_SOURCE_DIR}/js-elapsed-timer.cpp
-		${irccd_SOURCE_DIR}/js-elapsed-timer.h
-		${irccd_SOURCE_DIR}/js-file.cpp
-		${irccd_SOURCE_DIR}/js-file.h
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-logger.cpp
-		${irccd_SOURCE_DIR}/js-logger.h
-		${irccd_SOURCE_DIR}/js-plugin.cpp
-		${irccd_SOURCE_DIR}/js-plugin.h
-		${irccd_SOURCE_DIR}/js-server.cpp
-		${irccd_SOURCE_DIR}/js-system.cpp
-		${irccd_SOURCE_DIR}/js-system.h
-		${irccd_SOURCE_DIR}/js-timer.cpp
-		${irccd_SOURCE_DIR}/js-timer.h
-		${irccd_SOURCE_DIR}/js-unicode.cpp
-		${irccd_SOURCE_DIR}/js-util.cpp
-		${irccd_SOURCE_DIR}/js-util.h
-		${irccd_SOURCE_DIR}/plugin.cpp
-		${irccd_SOURCE_DIR}/plugin.h
-		${irccd_SOURCE_DIR}/rule.cpp
-		${irccd_SOURCE_DIR}/rule.h
-		${irccd_SOURCE_DIR}/server.cpp
-		${irccd_SOURCE_DIR}/server.h
-		${irccd_SOURCE_DIR}/server-state.cpp
-		${irccd_SOURCE_DIR}/server-state.h
-		${irccd_SOURCE_DIR}/timer.cpp
-		${irccd_SOURCE_DIR}/timer.h
-		${irccd_SOURCE_DIR}/transport-client.cpp
-		${irccd_SOURCE_DIR}/transport-client.h
-		${irccd_SOURCE_DIR}/transport-server.cpp
-		${irccd_SOURCE_DIR}/transport-server.h
-		main.cpp
+	SOURCES main.cpp
 	RESOURCES
 		${CMAKE_CURRENT_SOURCE_DIR}/timer-single.js
 		${CMAKE_CURRENT_SOURCE_DIR}/timer-repeat.js
 		${CMAKE_CURRENT_SOURCE_DIR}/timer-pending.js
-	LIBRARIES common extern-duktape extern-ircclient
+	LIBRARIES libirccd
 )
--- a/tests/js-timer/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-timer/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,9 +18,9 @@
 
 #include <gtest/gtest.h>
 
-#include <irccd.h>
-#include <elapsed-timer.h>
-#include <system.h>
+#include <irccd/elapsed-timer.h>
+#include <irccd/irccd.h>
+#include <irccd/system.h>
 
 using namespace irccd;
 
@@ -38,7 +38,7 @@
 		irccd.dispatch();
 	}
 
-	ASSERT_EQ(1, plugin->context().getGlobal<int>("count"));
+	ASSERT_EQ(1, duk::getGlobal<int>(plugin->context(), "count"));
 }
 
 TEST(Basic, repeat)
@@ -55,7 +55,7 @@
 		irccd.dispatch();
 	}
 
-	ASSERT_GE(plugin->context().getGlobal<int>("count"), 5);
+	ASSERT_GE(duk::getGlobal<int>(plugin->context(), "count"), 5);
 }
 
 #if 0
--- a/tests/js-unicode/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-unicode/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,13 +18,6 @@
 
 irccd_define_test(
 	NAME js-unicode
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.h
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-unicode.cpp
-		${irccd_SOURCE_DIR}/js-unicode.h
-		main.cpp
-	LIBRARIES common extern-duktape
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/js-unicode/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-unicode/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -22,24 +22,24 @@
 
 #include <gtest/gtest.h>
 
-#include <js-irccd.h>
-#include <js-unicode.h>
+#include <irccd/js-irccd.h>
+#include <irccd/js-unicode.h>
 
 using namespace irccd;
 
 TEST(TestJsUnicode, isLetter)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUnicode(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));"});
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));");
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 
-		ctx.peval(js::Script{"result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));"});
-		ASSERT_FALSE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));");
+		ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -47,17 +47,17 @@
 
 TEST(TestJsUnicode, isLower)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUnicode(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.Unicode.isLower(String('é').charCodeAt(0));"});
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));");
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 
-		ctx.peval(js::Script{"result = Irccd.Unicode.isLower(String('É').charCodeAt(0));"});
-		ASSERT_FALSE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));");
+		ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -65,17 +65,17 @@
 
 TEST(TestJsUnicode, isUpper)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUnicode(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));"});
-		ASSERT_TRUE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));");
+		ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result"));
 
-		ctx.peval(js::Script{"result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));"});
-		ASSERT_FALSE(ctx.getGlobal<bool>("result"));
+		duk::pevalString(ctx, "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));");
+		ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
--- a/tests/js-util/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-util/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,13 +18,6 @@
 
 irccd_define_test(
 	NAME js-util
-	SOURCES
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js.cpp
-		${irccd_SOURCE_DIR}/js-irccd.cpp
-		${irccd_SOURCE_DIR}/js-irccd.h
-		${irccd_SOURCE_DIR}/js-util.cpp
-		${irccd_SOURCE_DIR}/js-util.h
-		main.cpp
-	LIBRARIES common extern-duktape extern-ircclient
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/js-util/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/js-util/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,23 +18,25 @@
 
 #include <gtest/gtest.h>
 
-#include <js-irccd.h>
-#include <js-util.h>
+#include <irccd/js-irccd.h>
+#include <irccd/js-util.h>
 
 using namespace irccd;
 
 TEST(TestJsUtil, formatSimple)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUtil(ctx);
 
 	try {
-		ctx.peval(js::Script{
+		auto ret = duk::pevalString(ctx,
 			"result = Irccd.Util.format(\"#{target}\", { target: \"markand\" })"
-		});
+		);
 
+		if (ret != 0)
+			throw duk::error(ctx, -1);
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -42,15 +44,16 @@
 
 TEST(TestJsUtil, splituser)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUtil(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");"});
+		if (duk::pevalString(ctx, "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0)
+			throw duk::error(ctx, -1);
 
-		ASSERT_EQ("user", ctx.getGlobal<std::string>("result"));
+		ASSERT_EQ("user", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
@@ -58,15 +61,16 @@
 
 TEST(TestJsUtil, splithost)
 {
-	js::Context ctx;
+	duk::Context ctx;
 
 	loadJsIrccd(ctx);
 	loadJsUtil(ctx);
 
 	try {
-		ctx.peval(js::Script{"result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");"});
+		if (duk::pevalString(ctx, "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0)
+			throw duk::error(ctx, -1);
 
-		ASSERT_EQ("!~user@hyper/super/host", ctx.getGlobal<std::string>("result"));
+		ASSERT_EQ("!~user@hyper/super/host", duk::getGlobal<std::string>(ctx, "result"));
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
--- a/tests/path/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/path/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -19,5 +19,5 @@
 irccd_define_test(
 	NAME path
 	SOURCES main.cpp
-	LIBRARIES common
+	LIBRARIES libirccd
 )
--- a/tests/path/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/path/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -20,8 +20,8 @@
 
 #include <irccd-config.h>
 
-#include <logger.h>
-#include <path.h>
+#include <irccd/logger.h>
+#include <irccd/path.h>
 
 namespace irccd {
 
--- a/tests/rules/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/rules/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,9 +18,6 @@
 
 irccd_define_test(
 	NAME rules
-	SOURCES
-		${irccd_SOURCE_DIR}/rule.cpp
-		${irccd_SOURCE_DIR}/rule.h
-		main.cpp
-	LIBRARIES common
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/rules/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/rules/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,7 +18,7 @@
 
 #include <gtest/gtest.h>
 
-#include <rule.h>
+#include <irccd/rule.h>
 
 namespace irccd {
 
--- a/tests/timer/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/timer/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -18,9 +18,6 @@
 
 irccd_define_test(
 	NAME timer
-	SOURCES
-		${irccd_SOURCE_DIR}/timer.cpp
-		${irccd_SOURCE_DIR}/timer.h
-		main.cpp
-	LIBRARIES common
+	SOURCES main.cpp
+	LIBRARIES libirccd
 )
--- a/tests/timer/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/timer/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,8 +18,8 @@
 
 #include <gtest/gtest.h>
 
-#include <elapsed-timer.h>
-#include <timer.h>
+#include <irccd/elapsed-timer.h>
+#include <irccd/timer.h>
 
 using namespace irccd;
 using namespace std::chrono_literals;
--- a/tests/util/CMakeLists.txt	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/util/CMakeLists.txt	Wed Mar 30 13:52:47 2016 +0200
@@ -19,5 +19,5 @@
 irccd_define_test(
 	NAME util
 	SOURCES main.cpp
-	LIBRARIES common
+	LIBRARIES libirccd
 )
--- a/tests/util/main.cpp	Sat Mar 26 14:41:53 2016 +0100
+++ b/tests/util/main.cpp	Wed Mar 30 13:52:47 2016 +0200
@@ -18,8 +18,8 @@
 
 #include <gtest/gtest.h>
 
-#include <util.h>
-#include <system.h>
+#include <irccd/util.h>
+#include <irccd/system.h>
 
 namespace irccd {