changeset 72:98ac3c79009f

Irccd: start making a library, #429
author David Demelier <markand@malikania.fr>
date Thu, 24 Mar 2016 14:07:30 +0100
parents d7d0e239fb0e
children 8ee1178f1219
files CMakeLists.txt cmake/function/IrccdDefineExecutable.cmake cmake/function/IrccdDefineLibrary.cmake common/CMakeLists.txt common/directory.cpp common/directory.h common/elapsed-timer.cpp common/elapsed-timer.h common/filesystem.cpp common/filesystem.h common/ini.cpp common/ini.h common/json.cpp common/json.h common/logger.cpp common/logger.h common/options.cpp common/options.h common/path.cpp common/path.h common/signals.h common/sockets.cpp common/sockets.h common/system.cpp common/system.h common/unicode.cpp common/unicode.h common/util.cpp common/util.h common/xdg.cpp common/xdg.h extern/duktape/CMakeLists.txt extern/jansson/CMakeLists.txt extern/libircclient/CMakeLists.txt irccd/CMakeLists.txt irccd/command-plugin-info.cpp irccd/command-plugin-info.h irccd/command-plugin-list.cpp irccd/command-plugin-list.h irccd/command-plugin-load.cpp irccd/command-plugin-load.h irccd/command-plugin-reload.cpp irccd/command-plugin-reload.h irccd/command-plugin-unload.cpp irccd/command-plugin-unload.h irccd/command-server-cmode.cpp irccd/command-server-cmode.h irccd/command-server-cnotice.cpp irccd/command-server-cnotice.h irccd/command-server-connect.cpp irccd/command-server-connect.h irccd/command-server-disconnect.cpp irccd/command-server-disconnect.h irccd/command-server-info.cpp irccd/command-server-info.h irccd/command-server-invite.cpp irccd/command-server-invite.h irccd/command-server-join.cpp irccd/command-server-join.h irccd/command-server-kick.cpp irccd/command-server-kick.h irccd/command-server-list.cpp irccd/command-server-list.h irccd/command-server-me.cpp irccd/command-server-me.h irccd/command-server-message.cpp irccd/command-server-message.h irccd/command-server-mode.cpp irccd/command-server-mode.h irccd/command-server-nick.cpp irccd/command-server-nick.h irccd/command-server-notice.cpp irccd/command-server-notice.h irccd/command-server-part.cpp irccd/command-server-part.h irccd/command-server-reconnect.cpp irccd/command-server-reconnect.h irccd/command-server-topic.cpp irccd/command-server-topic.h irccd/config.cpp irccd/config.h irccd/irccd.cpp irccd/irccd.h irccd/js-directory.cpp irccd/js-directory.h irccd/js-elapsed-timer.cpp irccd/js-elapsed-timer.h irccd/js-file.cpp irccd/js-file.h irccd/js-irccd.cpp irccd/js-irccd.h irccd/js-logger.cpp irccd/js-logger.h irccd/js-plugin.cpp irccd/js-plugin.h irccd/js-server.cpp irccd/js-server.h irccd/js-system.cpp irccd/js-system.h irccd/js-timer.cpp irccd/js-timer.h irccd/js-unicode.cpp irccd/js-unicode.h irccd/js-util.cpp irccd/js-util.h irccd/js.cpp irccd/js.h irccd/main.cpp irccd/plugin.cpp irccd/plugin.h irccd/rule.cpp irccd/rule.h irccd/server-state.cpp irccd/server-state.h irccd/server.cpp irccd/server.h irccd/timer.cpp irccd/timer.h irccd/transport-client.cpp irccd/transport-client.h irccd/transport-command.h irccd/transport-server.cpp irccd/transport-server.h irccdctl/CMakeLists.txt irccdctl/alias.cpp irccdctl/alias.h irccdctl/command-help.cpp irccdctl/command-help.h irccdctl/command-plugin-info.cpp irccdctl/command-plugin-info.h irccdctl/command-plugin-list.cpp irccdctl/command-plugin-list.h irccdctl/command-plugin-load.cpp irccdctl/command-plugin-load.h irccdctl/command-plugin-reload.cpp irccdctl/command-plugin-reload.h irccdctl/command-plugin-unload.cpp irccdctl/command-plugin-unload.h irccdctl/command-server-cmode.cpp irccdctl/command-server-cmode.h irccdctl/command-server-cnotice.cpp irccdctl/command-server-cnotice.h irccdctl/command-server-connect.cpp irccdctl/command-server-connect.h irccdctl/command-server-disconnect.cpp irccdctl/command-server-disconnect.h irccdctl/command-server-info.cpp irccdctl/command-server-info.h irccdctl/command-server-invite.cpp irccdctl/command-server-invite.h irccdctl/command-server-join.cpp irccdctl/command-server-join.h irccdctl/command-server-kick.cpp irccdctl/command-server-kick.h irccdctl/command-server-list.cpp irccdctl/command-server-list.h irccdctl/command-server-me.cpp irccdctl/command-server-me.h irccdctl/command-server-message.cpp irccdctl/command-server-message.h irccdctl/command-server-mode.cpp irccdctl/command-server-mode.h irccdctl/command-server-nick.cpp irccdctl/command-server-nick.h irccdctl/command-server-notice.cpp irccdctl/command-server-notice.h irccdctl/command-server-part.cpp irccdctl/command-server-part.h irccdctl/command-server-reconnect.cpp irccdctl/command-server-reconnect.h irccdctl/command-server-topic.cpp irccdctl/command-server-topic.h irccdctl/command-watch.cpp irccdctl/command-watch.h irccdctl/command.h irccdctl/connection.cpp irccdctl/connection.h irccdctl/irccdctl.cpp irccdctl/irccdctl.h irccdctl/main.cpp lib/CMakeLists.txt lib/irccd/CMakeSources.cmake lib/irccd/alias.cpp lib/irccd/alias.h lib/irccd/application.cpp lib/irccd/application.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/config.h lib/irccd/irccd.cpp lib/irccd/irccd.h lib/irccd/irccdctl.cpp lib/irccd/irccdctl.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/json.cpp lib/irccd/json.h lib/irccd/logger.cpp lib/irccd/logger.h lib/irccd/options.cpp lib/irccd/options.h lib/irccd/path.cpp lib/irccd/path.h 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/rule.cpp lib/irccd/rule.h lib/irccd/server-state.cpp lib/irccd/server-state.h lib/irccd/server.cpp lib/irccd/server.h lib/irccd/system.cpp lib/irccd/system.h lib/irccd/timer.cpp lib/irccd/timer.h lib/irccd/transport-client.cpp lib/irccd/transport-client.h lib/irccd/transport-server.cpp lib/irccd/transport-server.h lib/irccd/unicode.cpp lib/irccd/unicode.h lib/irccd/util.cpp lib/irccd/util.h
diffstat 312 files changed, 32411 insertions(+), 33608 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -34,11 +34,11 @@
 #
 # Build system is then processed in different directories:
 #
-# common			- Static common library between irccd and irccdctl.
 # contrib			- User contributions not maintained by irccd authors.
 # doc				- The documentation process.
 # extern			- External libraries.
-# irccd				- The main irccd executable.
+# lib				- The irccd library
+# irccd				- The irccd executable.
 # irccdctl			- The irccdctl utility.
 # plugins			- Official irccd plugins.
 # tests				- The unit tests.
@@ -67,8 +67,8 @@
 
 add_subdirectory(extern/libircclient)
 add_subdirectory(extern/jansson)
-add_subdirectory(common)
 add_subdirectory(doc)
+add_subdirectory(lib)
 add_subdirectory(irccd)
 add_subdirectory(irccdctl)
 add_subdirectory(contrib)
@@ -122,20 +122,20 @@
 message("")
 
 # CPack (only for package_source, package_ifw is home made).
-include(cmake/IrccdPackage.cmake)
-include(CPack)
+#include(cmake/IrccdPackage.cmake)
+#include(CPack)
 
 # Meta release target.
-if (IRCCD_PACKAGE)
-	add_custom_target(
-		release
-		COMMENT "Releasing irccd ${IRCCD_VERSION}"
-		COMMAND
-			${CMAKE_MAKE_PROGRAM} package_source
-	)
-
-	add_dependencies(release package_ifw)
-endif ()
+#if (IRCCD_PACKAGE)
+#	add_custom_target(
+#		release
+#		COMMENT "Releasing irccd ${IRCCD_VERSION}"
+#		COMMAND
+#			${CMAKE_MAKE_PROGRAM} package_source
+#	)
+#
+#	add_dependencies(release package_ifw)
+#endif ()
 
 # Show warnings about non-relocatable irccd.
 if (NOT IRCCD_RELOCATABLE)
--- a/cmake/function/IrccdDefineExecutable.cmake	Mon Mar 14 11:24:25 2016 +0100
+++ b/cmake/function/IrccdDefineExecutable.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -35,7 +35,7 @@
 
 function(irccd_define_executable)
 	set(options INSTALL PRIVATE)
-	set(oneValueArgs TARGET INSTALL)
+	set(oneValueArgs TARGET)
 	set(multiValueArgs SOURCES FLAGS LIBRARIES INCLUDES)
 
 	cmake_parse_arguments(EXE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
@@ -76,4 +76,4 @@
 			RUNTIME DESTINATION ${WITH_BINDIR}
 		)
 	endif ()
-endfunction()
\ No newline at end of file
+endfunction()
--- a/cmake/function/IrccdDefineLibrary.cmake	Mon Mar 14 11:24:25 2016 +0100
+++ b/cmake/function/IrccdDefineLibrary.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -23,6 +23,7 @@
 # irccd_define_library(
 #	TARGET target name
 #	SOURCES src1, src2, srcn
+#	LOCAL (Optional) set to true to build a static library
 #	FLAGS (Optional) C/C++ flags (without -D)
 #	LIBRARIES (Optional) libraries to link
 #	LOCAL_INCLUDES (Optional) local includes for the target only
@@ -33,11 +34,12 @@
 #
 
 function(irccd_define_library)
+	set(options LOCAL)
 	set(oneValueArgs TARGET)
 	set(multiValueArgs SOURCES FLAGS LIBRARIES LOCAL_INCLUDES PUBLIC_INCLUDES)
 	set(mandatory TARGET SOURCES)
 
-	cmake_parse_arguments(LIB "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+	cmake_parse_arguments(LIB "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
 	if (NOT LIB_TARGET)
 		message(FATAL_ERROR "Please set TARGET")
@@ -45,9 +47,12 @@
 	if (NOT LIB_SOURCES)
 		message(FATAL_ERROR "Please set SOURCES")
 	endif ()
+	if (LIB_LOCAL)
+		set(type STATIC)
+	endif ()
 
-	add_library(${LIB_TARGET} STATIC ${LIB_SOURCES})
+	add_library(${LIB_TARGET} ${type} ${LIB_SOURCES})
 	target_include_directories(${LIB_TARGET} PRIVATE ${LIB_LOCAL_INCLUDES} PUBLIC ${LIB_PUBLIC_INCLUDES})
 	target_compile_definitions(${LIB_TARGET} PUBLIC ${LIB_FLAGS})
 	target_link_libraries(${LIB_TARGET} ${LIB_LIBRARIES})
-endfunction()
\ No newline at end of file
+endfunction()
--- a/common/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for 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.
-#
-
-project(common)
-
-set(
-	SOURCES
-	directory.cpp
-	directory.h
-	elapsed-timer.cpp
-	elapsed-timer.h
-	filesystem.cpp
-	filesystem.h
-	ini.cpp
-	ini.h
-	json.cpp
-	json.h
-	logger.cpp
-	logger.h
-	options.cpp
-	options.h
-	path.cpp
-	path.h
-	signals.h
-	sockets.cpp
-	sockets.h
-	system.h
-	system.cpp
-	unicode.cpp
-	unicode.h
-	util.cpp
-	util.h
-)
-
-if (UNIX)
-	list(APPEND SOURCES xdg.cpp xdg.h)
-
-	if (IRCCD_SYSTEM_LINUX)
-		list(APPEND LIBRARIES dl)
-	endif ()
-elseif (WIN32)
-	list(APPEND LIBRARIES ws2_32)
-	list(APPEND LIBRARIES gdi32)
-	list(APPEND LIBRARIES shlwapi)
-endif ()
-
-irccd_define_library(
-	TARGET common
-	SOURCES ${SOURCES}
-	LIBRARIES
-		extern-jansson
-		${LIBRARIES}
-	LOCAL_INCLUDES ${INCLUDES}
-	PUBLIC_INCLUDES
-		${CMAKE_BINARY_DIR}
-		${common_SOURCE_DIR}
-		${Jansson_INCLUDE_DIRS}
-)
-
-target_compile_definitions(common PUBLIC SOCKET_NO_SSL)
--- a/common/directory.cpp	Mon Mar 14 11:24:25 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/common/directory.h	Mon Mar 14 11:24:25 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/common/elapsed-timer.cpp	Mon Mar 14 11:24:25 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/common/elapsed-timer.h	Mon Mar 14 11:24:25 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/common/filesystem.cpp	Mon Mar 14 11:24:25 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/common/filesystem.h	Mon Mar 14 11:24:25 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/common/ini.cpp	Mon Mar 14 11:24:25 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/common/ini.h	Mon Mar 14 11:24:25 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/common/json.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,369 +0,0 @@
-/*
- * json.cpp -- C++14 JSON manipulation using jansson parser
- *
- * 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 <jansson.h>
-
-#include <sstream>
-
-#include "json.h"
-
-namespace irccd {
-
-namespace json {
-
-namespace {
-
-void readObject(Value &parent, json_t *object);
-void readArray(Value &parent, json_t *array);
-
-Value readValue(json_t *v)
-{
-	if (json_is_null(v))
-		return Value(nullptr);
-	if (json_is_string(v))
-		return Value(json_string_value(v));
-	if (json_is_real(v))
-		return Value(json_number_value(v));
-	if (json_is_integer(v))
-		return Value(static_cast<int>(json_integer_value(v)));
-	if (json_is_boolean(v))
-		return Value(json_boolean_value(v));
-	if (json_is_object(v)) {
-		Value object(Type::Object);
-
-		readObject(object, v);
-
-		return object;
-	}
-	if (json_is_array(v)) {
-		Value array(Type::Array);
-
-		readArray(array, v);
-
-		return array;
-	}
-
-	return Value();
-}
-
-void readObject(Value &parent, json_t *object)
-{
-	const char *key;
-	json_t *value;
-
-	json_object_foreach(object, key, value)
-		parent.insert(key, readValue(value));
-}
-
-void readArray(Value &parent, json_t *array)
-{
-	size_t index;
-	json_t *value;
-
-	json_array_foreach(array, index, value)
-		parent.append(readValue(value));
-}
-
-template <typename Func, typename... Args>
-Value convert(Func fn, Args&&... args)
-{
-	json_error_t error;
-	json_t *json = fn(std::forward<Args>(args)..., &error);
-
-	if (json == nullptr)
-		throw Error(error.text, error.source, error.line, error.column, error.position);
-
-	Value value;
-
-	if (json_is_object(json)) {
-		value = Value(Type::Object);
-		readObject(value, json);
-	} else {
-		value = Value(Type::Array);
-		readArray(value, json);
-	}
-
-	json_decref(json);
-
-	return value;
-}
-
-std::string indent(int param, int level)
-{
-	std::string str;
-
-	if (param < 0)
-		str = std::string(level, '\t');
-	else if (param > 0)
-		str = std::string(param * level, ' ');
-
-	return str;
-}
-
-} // !namespace
-
-void Value::copy(const Value &other)
-{
-	switch (other.m_type) {
-	case Type::Array:
-		new (&m_array) std::vector<Value>(other.m_array);
-		break;
-	case Type::Boolean:
-		m_boolean = other.m_boolean;
-		break;
-	case Type::Int:
-		m_integer = other.m_integer;
-		break;
-	case Type::Object:
-		new (&m_object) std::map<std::string, Value>(other.m_object);
-		break;
-	case Type::Real:
-		m_number = other.m_number;
-		break;
-	case Type::String:
-		new (&m_string) std::string(other.m_string);
-		break;
-	default:
-		break;
-	}
-
-	m_type = other.m_type;
-}
-
-void Value::move(Value &&other)
-{
-	switch (other.m_type) {
-	case Type::Array:
-		new (&m_array) std::vector<Value>(std::move(other.m_array));
-		break;
-	case Type::Boolean:
-		m_boolean = other.m_boolean;
-		break;
-	case Type::Int:
-		m_integer = other.m_integer;
-		break;
-	case Type::Object:
-		new (&m_object) std::map<std::string, Value>(std::move(other.m_object));
-		break;
-	case Type::Real:
-		m_number = other.m_number;
-		break;
-	case Type::String:
-		new (&m_string) std::string(std::move(other.m_string));
-		break;
-	default:
-		break;
-	}
-
-	m_type = other.m_type;
-}
-
-Value::Value(Type type)
-	: m_type(type)
-{
-	switch (m_type) {
-	case Type::Array:
-		new (&m_array) std::vector<Value>();
-		break;
-	case Type::Boolean:
-		m_boolean = false;
-		break;
-	case Type::Int:
-		m_integer = 0;
-		break;
-	case Type::Object:
-		new (&m_object) std::map<std::string, Value>();
-		break;
-	case Type::Real:
-		m_number = 0;
-		break;
-	case Type::String:
-		new (&m_string) std::string();
-		break;
-	default:
-		break;
-	}
-}
-
-Value::~Value()
-{
-	switch (m_type) {
-	case Type::Array:
-		m_array.~vector<Value>();
-		break;
-	case Type::Object:
-		m_object.~map<std::string, Value>();
-		break;
-	case Type::String:
-		m_string.~basic_string();
-		break;
-	default:
-		break;
-	}
-}
-
-bool Value::toBool() const noexcept
-{
-	if (m_type != Type::Boolean)
-		return false;
-
-	return m_boolean;
-}
-
-double Value::toReal() const noexcept
-{
-	if (m_type != Type::Real)
-		return 0;
-
-	return m_number;
-}
-
-int Value::toInt() const noexcept
-{
-	if (m_type != Type::Int)
-		return 0;
-
-	return m_integer;
-}
-
-std::string Value::toString(bool coerce) const noexcept
-{
-	std::string result;
-
-	if (m_type == Type::String)
-		result = m_string;
-	else if (coerce)
-		result = toJson();
-
-	return result;
-}
-
-Value::Value(const Buffer &buffer)
-{
-	*this = convert(json_loads, buffer.text.c_str(), 0);
-}
-
-Value::Value(const File &file)
-{
-	*this = convert(json_load_file, file.path.c_str(), 0);
-}
-
-std::string Value::toJson(int level, int current) const
-{
-	std::ostringstream oss;
-
-	switch (m_type) {
-	case Type::Array: {
-		oss << '[' << (level != 0 ? "\n" : "");
-
-		unsigned total = m_array.size();
-		unsigned i = 0;
-		for (const auto &v : m_array) {
-			oss << indent(level, current + 1) << v.toJson(level, current + 1);
-			oss << (++i < total ? "," : "");
-			oss << (level != 0 ? "\n" : "");
-		}
-
-		oss << (level != 0 ? indent(level, current) : "") << ']';
-		break;
-	}
-	case Type::Boolean:
-		oss << (m_boolean ? "true" : "false");
-		break;
-	case Type::Int:
-		oss << m_integer;
-		break;
-	case Type::Null:
-		oss << "null";
-		break;
-	case Type::Object: {
-		oss << '{' << (level != 0 ? "\n" : "");
-
-		unsigned total = m_object.size();
-		unsigned i = 0;
-		for (const auto &pair : m_object) {
-			oss << indent(level, current + 1);
-
-			/* Key and : */
-			oss << "\"" << pair.first << "\":" << (level != 0 ? " " : "");
-
-			/* Value */
-			oss << pair.second.toJson(level, current + 1);
-
-			/* Comma, new line if needed */
-			oss << (++i < total ? "," : "") << (level != 0 ? "\n" : "");
-		}
-
-		oss << (level != 0 ? indent(level, current) : "") << '}';
-		break;
-	}
-	case Type::Real:
-		oss << m_number;
-		break;
-	case Type::String:
-		oss << "\"" << escape(m_string) << "\"";
-		break;
-	default:
-		break;
-	}
-
-	return oss.str();
-}
-
-std::string escape(const std::string &value)
-{
-	std::string result;
-
-	for (auto it = value.begin(); it != value.end(); ++it) {
-		switch (*it) {
-		case '\\':
-			result += "\\\\";
-			break;
-		case '/':
-			result += "\\/";
-			break;
-		case '"':
-			result += "\\\"";
-			break;
-		case '\b':
-			result += "\\b";
-			break;
-		case '\f':
-			result += "\\f";
-			break;
-		case '\n':
-			result += "\\n";
-			break;
-		case '\r':
-			result += "\\r";
-			break;
-		case '\t':
-			result += "\\t";
-			break;
-		default:
-			result += *it;
-			break;
-		}
-	}
-
-	return result;
-}
-
-} // !json
-
-} // !irccd
--- a/common/json.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1183 +0,0 @@
-/*
- * json.h -- C++14 JSON manipulation using jansson parser
- *
- * 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 _JSON_H_
-#define _JSON_H_
-
-/**
- * @file json.h
- * @brief Jansson C++14 wrapper
- *
- * These classes can be used to build or parse JSON documents using jansson library. It is designed to be safe
- * and explicit. It does not implement implicit sharing like jansson so when you access (e.g. Value::toObject) values
- * you get real copies, thus when you read big documents it can has a performance cost.
- */
-
-#include <cassert>
-#include <exception>
-#include <initializer_list>
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-namespace irccd {
-
-/**
- * Json namespace.
- */
-namespace json {
-
-/**
- * @enum Type
- * @brief Type of Value.
- */
-enum class Type {
-	Array,		//!< Value is an array []
-	Boolean,	//!< Value is boolean
-	Int,		//!< Value is integer
-	Null,		//!< Value is defined to null
-	Object,		//!< Value is object {}
-	Real,		//!< Value is float
-	String		//!< Value is unicode string
-};
-
-/**
- * @class Error
- * @brief Error description.
- */
-class Error : public std::exception {
-private:
-	std::string m_text;
-	std::string m_source;
-	int m_line;
-	int m_column;
-	int m_position;
-
-public:
-	/**
-	 * Create the error.
-	 *
-	 * @param text the text message
-	 * @param source the source (e.g. file name)
-	 * @param line the line number
-	 * @param column the column number
-	 * @param position the position
-	 */
-	inline Error(std::string text, std::string source, int line, int column, int position) noexcept
-		: m_text(std::move(text))
-		, m_source(std::move(source))
-		, m_line(line)
-		, m_column(column)
-		, m_position(position)
-	{
-	}
-
-	/**
-	 * Get the error message.
-	 *
-	 * @return the text
-	 */
-	inline const std::string &text() const noexcept
-	{
-		return m_text;
-	}
-
-	/**
-	 * Get the source (e.g. a file name).
-	 *
-	 * @return the source
-	 */
-	inline const std::string &source() const noexcept
-	{
-		return m_source;
-	}
-
-	/**
-	 * 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 position.
-	 *
-	 * @return the position
-	 */
-	inline int position() const noexcept
-	{
-		return m_position;
-	}
-
-	/**
-	 * Get the error message.
-	 *
-	 * @return the message
-	 */
-	const char *what() const noexcept override
-	{
-		return m_text.c_str();
-	}
-};
-
-/**
- * @class Buffer
- * @brief Open JSON document from text.
- */
-class Buffer {
-public:
-	std::string text;	//!< The JSON text
-};
-
-/**
- * @class File
- * @brief Open JSON document from a file.
- */
-class File {
-public:
-	std::string path;	//!< The path to the file
-};
-
-/**
- * @class Value
- * @brief Generic JSON value wrapper.
- */
-class Value {
-private:
-	Type m_type{Type::Null};
-
-	union {
-		double m_number;
-		bool m_boolean;
-		int m_integer;
-		std::string m_string;
-		std::vector<Value> m_array;
-		std::map<std::string, Value> m_object;
-	};
-
-	void copy(const Value &);
-	void move(Value &&);
-	std::string toJson(int indent, int current) const;
-
-	/**
-	 * @class BaseIterator
-	 * @brief This is the base class for iterator and const_iterator
-	 *
-	 * This iterator works for both arrays and objects. Because of that purpose, it is only available
-	 * as forward iterator.
-	 *
-	 * When iterator comes from an object, you can use key() otherwise you can use index().
-	 */
-	template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType>
-	class BaseIterator : public std::iterator<std::forward_iterator_tag, ValueType> {
-	private:
-		friend class Value;
-
-		ValueType &m_value;
-		ArrayIteratorType m_ita;
-		ObjectIteratorType m_itm;
-
-		inline void increment()
-		{
-			if (m_value.isObject())
-				m_itm++;
-			else
-				m_ita++;
-		}
-
-		BaseIterator(ValueType &value, ObjectIteratorType it)
-			: m_value(value)
-			, m_itm(it)
-		{
-		}
-
-		BaseIterator(ValueType &value, ArrayIteratorType it)
-			: m_value(value)
-			, m_ita(it)
-		{
-		}
-
-	public:
-		/**
-		 * Get the iterator key (for objects).
-		 *
-		 * @pre iterator must be dereferenceable
-		 * @pre iterator must come from object
-		 * @return the key
-		 */
-		inline const std::string &key() const noexcept
-		{
-			assert(m_value.isObject());
-			assert(m_itm != m_value.m_object.end());
-
-			return m_itm->first;
-		}
-
-		/**
-		 * Get the iterator position (for arrays).
-		 *
-		 * @pre iterator must be dereferenceable
-		 * @pre iterator must come from arrays
-		 * @return the index
-		 */
-		inline unsigned index() const noexcept
-		{
-			assert(m_value.isArray());
-			assert(m_ita != m_value.m_array.end());
-
-			return std::distance(m_value.m_array.begin(), m_ita);
-		}
-
-		/**
-		 * Dereference the iterator.
-		 *
-		 * @pre iterator be dereferenceable
-		 * @return the value
-		 */
-		inline ValueType &operator*() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			return (m_value.m_type == Type::Object) ? m_itm->second : *m_ita;
-		}
-
-		/**
-		 * Dereference the iterator as a pointer.
-		 *
-		 * @pre iterator must be dereferenceable
-		 * @return the value
-		 */
-		inline ValueType *operator->() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			return (m_value.m_type == Type::Object) ? &m_itm->second : &(*m_ita);
-		}
-
-		/**
-		 * Increment the iterator. (Prefix version).
-		 *
-		 * @pre iterator must be dereferenceable
-		 * @return *this;
-		 */
-		inline BaseIterator &operator++() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			increment();
-
-			return *this;
-		}
-
-		/**
-		 * Increment the iterator. (Postfix version).
-		 *
-		 * @pre iterator must be dereferenceable
-		 * @return *this;
-		 */
-		inline BaseIterator &operator++(int) noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			increment();
-
-			return *this;
-		}
-
-		/**
-		 * Compare two iterators.
-		 *
-		 * @param it1 the first iterator
-		 * @param it2 the second iterator
-		 * @return true if they are same
-		 */
-		bool operator==(const BaseIterator &it) const noexcept
-		{
-			if (m_value.isObject() && it.m_value.isObject())
-				return m_itm == it.m_itm;
-			if (m_value.isArray() && it.m_value.isArray())
-				return m_ita == it.m_ita;
-
-			return false;
-		}
-
-		/**
-		 * Test if the iterator is different.
-		 *
-		 * @param it the iterator
-		 * @return true if they are different
-		 */
-		inline bool operator!=(const BaseIterator &it) const noexcept
-		{
-			return !(*this == it);
-		}
-	};
-
-public:
-	/**
-	 * Forward iterator.
-	 */
-	using iterator = BaseIterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>;
-
-	/**
-	 * Const forward iterator.
-	 */
-	using const_iterator = BaseIterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>;
-
-	/**
-	 * Construct a null value.
-	 */
-	inline Value()
-	{
-	}
-
-	/**
-	 * Create a value with a specified type, this is usually only needed when you want to create an object or
-	 * an array.
-	 *
-	 * For any other types, initialize with sane default value.
-	 *
-	 * @param type the type
-	 */
-	Value(Type type);
-
-	/**
-	 * Construct a null value.
-	 */
-	inline Value(std::nullptr_t) noexcept
-		: m_type(Type::Null)
-	{
-	}
-
-	/**
-	 * Construct a boolean value.
-	 *
-	 * @param value the boolean value
-	 */
-	inline Value(bool value) noexcept
-		: m_type(Type::Boolean)
-		, m_boolean(value)
-	{
-	}
-
-	/**
-	 * Create value from integer.
-	 *
-	 * @param value the value
-	 */
-	inline Value(int value) noexcept
-		: m_type(Type::Int)
-		, m_integer(value)
-	{
-	}
-
-	/**
-	 * Construct a value from a C-string.
-	 *
-	 * @param value the C-string
-	 */
-	inline Value(const char *value)
-		: m_type(Type::String)
-	{
-		new (&m_string) std::string(value ? value : "");
-	}
-
-	/**
-	 * Construct a number value.
-	 *
-	 * @param value the real value
-	 */
-	inline Value(double value) noexcept
-		: m_type(Type::Real)
-		, m_number(value)
-	{
-	}
-
-	/**
-	 * Construct a string value.
-	 *
-	 * @param value the string
-	 */
-	inline Value(std::string value) noexcept
-		: m_type(Type::String)
-	{
-		new (&m_string) std::string(std::move(value));
-	}
-
-	/**
-	 * Create an object from a map.
-	 *
-	 * @param values the values
-	 * @see fromObject
-	 */
-	inline Value(std::map<std::string, Value> values)
-		: Value(Type::Object)
-	{
-		for (const auto &pair : values)
-			insert(pair.first, pair.second);
-	}
-
-	/**
-	 * Create an array from a vector.
-	 *
-	 * @param values the values
-	 * @see fromArray
-	 */
-	inline Value(std::vector<Value> values)
-		: Value(Type::Array)
-	{
-		for (Value value : values)
-			append(std::move(value));
-	}
-
-	/**
-	 * Construct a value from a buffer.
-	 *
-	 * @param buffer the text
-	 * @throw Error on errors
-	 */
-	Value(const Buffer &buffer);
-
-	/**
-	 * Construct a value from a file.
-	 *
-	 * @param file the file
-	 * @throw Error on errors
-	 */
-	Value(const File &file);
-
-	/**
-	 * Move constructor.
-	 *
-	 * @param other the value to move from
-	 */
-	inline Value(Value &&other)
-	{
-		move(std::move(other));
-	}
-
-	/**
-	 * Copy constructor.
-	 *
-	 * @param other the value to copy from
-	 */
-	inline Value(const Value &other)
-	{
-		copy(other);
-	}
-
-	/**
-	 * Copy operator.
-	 *
-	 * @param other the value to copy from
-	 * @return *this
-	 */
-	inline Value &operator=(const Value &other)
-	{
-		copy(other);
-
-		return *this;
-	}
-
-	/**
-	 * Move operator.
-	 *
-	 * @param other the value to move from
-	 */
-	inline Value &operator=(Value &&other)
-	{
-		move(std::move(other));
-
-		return *this;
-	}
-
-	/**
-	 * Destructor.
-	 */
-	~Value();
-
-	/**
-	 * Get an iterator to the beginning.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline iterator begin() noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? iterator(*this, m_object.begin()) : iterator(*this, m_array.begin());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline const_iterator begin() const noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? const_iterator(*this, m_object.begin()) : const_iterator(*this, m_array.begin());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline const_iterator cbegin() const noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? const_iterator(*this, m_object.cbegin()) : const_iterator(*this, m_array.cbegin());
-	}
-
-	/**
-	 * Get an iterator to the end.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline iterator end() noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? iterator(*this, m_object.end()) : iterator(*this, m_array.end());
-	}
-
-	/**
-	 * Get an iterator to the end.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline const_iterator end() const noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? const_iterator(*this, m_object.end()) : const_iterator(*this, m_array.end());
-	}
-
-	/**
-	 * Get an iterator to the end.
-	 *
-	 * @pre must be an array or object
-	 * @return the iterator
-	 */
-	inline const_iterator cend() const noexcept
-	{
-		assert(isArray() || isObject());
-
-		return m_type == Type::Object ? const_iterator(*this, m_object.cend()) : const_iterator(*this, m_array.cend());
-	}
-
-	/**
-	 * Get the value type.
-	 *
-	 * @return the type
-	 */
-	inline Type typeOf() const noexcept
-	{
-		return m_type;
-	}
-
-	/**
-	 * Get the value as boolean.
-	 *
-	 * @return the value or false if not a boolean
-	 */
-	bool toBool() const noexcept;
-
-	/**
-	 * Get the value as integer.
-	 *
-	 * @return the value or 0 if not a integer
-	 */
-	int toInt() const noexcept;
-
-	/**
-	 * Get the value as real.
-	 *
-	 * @return the value or 0 if not a real
-	 */
-	double toReal() const noexcept;
-
-	/**
-	 * Get the value as string.
-	 *
-	 * @param coerce set to true to coerce the value if not a string
-	 * @return the value or empty string if not a string
-	 */
-	std::string toString(bool coerce = false) const noexcept;
-
-	/**
-	 * Check if the value is boolean type.
-	 *
-	 * @return true if boolean
-	 */
-	inline bool isBool() const noexcept
-	{
-		return m_type == Type::Boolean;
-	}
-
-	/**
-	 * Check if the value is integer type.
-	 *
-	 * @return true if integer
-	 */
-	inline bool isInt() const noexcept
-	{
-		return m_type == Type::Int;
-	}
-
-	/**
-	 * Check if the value is object type.
-	 *
-	 * @return true if object
-	 */
-	inline bool isObject() const noexcept
-	{
-		return m_type == Type::Object;
-	}
-
-	/**
-	 * Check if the value is array type.
-	 *
-	 * @return true if array
-	 */
-	inline bool isArray() const noexcept
-	{
-		return m_type == Type::Array;
-	}
-
-	/**
-	 * Check if the value is integer or real type.
-	 *
-	 * @return true if integer or real
-	 * @see toInt
-	 * @see toReal
-	 */
-	inline bool isNumber() const noexcept
-	{
-		return m_type == Type::Real || m_type == Type::Int;
-	}
-
-	/**
-	 * Check if the value is real type.
-	 *
-	 * @return true if real
-	 */
-	inline bool isReal() const noexcept
-	{
-		return m_type == Type::Real;
-	}
-
-	/**
-	 * Check if the value is null type.
-	 *
-	 * @return true if null
-	 */
-	inline bool isNull() const noexcept
-	{
-		return m_type == Type::Null;
-	}
-
-	/**
-	 * Check if the value is string type.
-	 *
-	 * @return true if string
-	 */
-	inline bool isString() const noexcept
-	{
-		return m_type == Type::String;
-	}
-
-	/**
-	 * Get the array or object size.
-	 *
-	 * @pre must be an array or object
-	 * @return the size
-	 */
-	inline unsigned size() const noexcept
-	{
-		assert(isArray() || isObject());
-
-		if (m_type == Type::Object)
-			return m_object.size();
-
-		return m_array.size();
-	}
-
-	/**
-	 * Remove all the values.
-	 *
-	 * @pre must be an array or an object
-	 */
-	inline void clear() noexcept
-	{
-		assert(isArray() || isObject());
-
-		if (m_type == Type::Array)
-			m_array.clear();
-		else
-			m_object.clear();
-	}
-
-	/*
-	 * Array functions
-	 * ----------------------------------------------------------
-	 */
-
-	/**
-	 * Get the value at the specified position or the defaultValue if position is out of bounds.
-	 *
-	 * @param position the position
-	 * @param defaultValue the value replacement
-	 * @return the value or defaultValue
-	 */
-	template <typename DefaultValue>
-	inline Value valueOr(unsigned position, DefaultValue &&defaultValue) const
-	{
-		if (m_type != Type::Array || position >= m_array.size())
-			return defaultValue;
-
-		return m_array[position];
-	}
-
-	/**
-	 * Overloaded function with type check.
-	 *
-	 * @param position the position
-	 * @param type the requested type
-	 * @param defaultValue the value replacement
-	 * @return the value or defaultValue
-	 */
-	template <typename DefaultValue>
-	inline Value valueOr(unsigned position, Type type, DefaultValue &&defaultValue) const
-	{
-		if (m_type != Type::Array || position >= m_array.size() || m_array[position].typeOf() != type)
-			return defaultValue;
-
-		return m_array[position];
-	}
-
-	/**
-	 * Get a value at the specified index.
-	 *
-	 * @pre must be an array
-	 * @param position the position
-	 * @return the value
-	 * @throw std::out_of_range if out of bounds
-	 */
-	inline const Value &at(unsigned position) const
-	{
-		assert(isArray());
-
-		return m_array.at(position);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array
-	 * @param position the position
-	 * @return the value
-	 * @throw std::out_of_range if out of bounds
-	 */
-	inline Value &at(unsigned position)
-	{
-		assert(isArray());
-
-		return m_array.at(position);
-	}
-
-	/**
-	 * Get a value at the specified index.
-	 *
-	 * @pre must be an array
-	 * @pre position must be valid
-	 * @param position the position
-	 * @return the value
-	 */
-	inline const Value &operator[](unsigned position) const
-	{
-		assert(isArray());
-		assert(position < m_array.size());
-
-		return m_array[position];
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array
-	 * @pre position must be valid
-	 * @param position the position
-	 * @return the value
-	 */
-	inline Value &operator[](unsigned position)
-	{
-		assert(isArray());
-		assert(position < m_array.size());
-
-		return m_array[position];
-	}
-
-	/**
-	 * Push a value to the beginning of the array.
-	 *
-	 * @pre must be an array
-	 * @param value the value to push
-	 */
-	inline void push(const Value &value)
-	{
-		assert(isArray());
-
-		m_array.insert(m_array.begin(), value);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array
-	 * @param value the value to push
-	 */
-	inline void push(Value &&value)
-	{
-		assert(isArray());
-
-		m_array.insert(m_array.begin(), std::move(value));
-	}
-
-	/**
-	 * Insert a value at the specified position.
-	 *
-	 * @pre must be an array
-	 * @pre position must be valid
-	 * @param position the position
-	 * @param value the value to push
-	 */
-	inline void insert(unsigned position, const Value &value)
-	{
-		assert(isArray());
-		assert(position <= m_array.size());
-
-		m_array.insert(m_array.begin() + position, value);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array
-	 * @pre position must be valid
-	 * @param position the position
-	 * @param value the value to push
-	 */
-	inline void insert(unsigned position, Value &&value)
-	{
-		assert(isArray());
-		assert(position <= m_array.size());
-
-		m_array.insert(m_array.begin() + position, std::move(value));
-	}
-
-	/**
-	 * Add a new value to the end.
-	 *
-	 * @pre must be an array
-	 * @param value the value to append
-	 */
-	inline void append(const Value &value)
-	{
-		assert(isArray());
-
-		m_array.push_back(value);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an array
-	 * @param value the value to append
-	 */
-	inline void append(Value &&value)
-	{
-		assert(isArray());
-
-		m_array.push_back(std::move(value));
-	}
-
-	/**
-	 * Remove a value at the specified position.
-	 *
-	 * @pre must be an array
-	 * @pre position must be valid
-	 * @param position the position
-	 */
-	inline void erase(unsigned position)
-	{
-		assert(isArray());
-		assert(position < m_array.size());
-
-		m_array.erase(m_array.begin() + position);
-	}
-
-	/*
-	 * Object functions
-	 * ----------------------------------------------------------
-	 */
-
-	/**
-	 * Get the value at the specified key or the defaultValue if key is absent.
-	 *
-	 * @param name the name
-	 * @param defaultValue the value replacement
-	 * @return the value or defaultValue
-	 */
-	template <typename DefaultValue>
-	Value valueOr(const std::string &name, DefaultValue &&defaultValue) const
-	{
-		if (m_type != Type::Object)
-			return defaultValue;
-
-		auto it = m_object.find(name);
-
-		if (it == m_object.end())
-			return defaultValue;
-
-		return it->second;
-	}
-
-	/**
-	 * Overloaded function with type check.
-	 *
-	 * @param name the name
-	 * @param type the requested type
-	 * @param defaultValue the value replacement
-	 * @return the value or defaultValue
-	 */
-	template <typename DefaultValue>
-	Value valueOr(const std::string &name, Type type, DefaultValue &&defaultValue) const
-	{
-		if (m_type != Type::Object)
-			return defaultValue;
-
-		auto it = m_object.find(name);
-
-		if (it == m_object.end() || it->second.typeOf() != type)
-			return defaultValue;
-
-		return it->second;
-	}
-
-	/**
-	 * Get a value from the object.
-	 *
-	 * @pre must be an object
-	 * @param name the value key
-	 * @return the value
-	 * @throw std::out_of_range if not found
-	 */
-	inline const Value &at(const std::string &name) const
-	{
-		assert(isObject());
-
-		return m_object.at(name);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an object
-	 * @param name the value key
-	 * @return the value
-	 * @throw std::out_of_range if not found
-	 */
-	inline Value &at(const std::string &name)
-	{
-		assert(isObject());
-
-		return m_object.at(name);
-	}
-
-	/**
-	 * Get a value from the object.
-	 *
-	 * @pre must be an object
-	 * @param name the value key
-	 * @return the value
-	 */
-	inline Value &operator[](const std::string &name)
-	{
-		assert(isObject());
-
-		return m_object[name];
-	}
-
-	/**
-	 * Find a value by key.
-	 *
-	 * @pre must be an object
-	 * @param key the property key
-	 * @return the iterator or past the end if not found
-	 */
-	inline iterator find(const std::string &key)
-	{
-		assert(isObject());
-
-		return iterator(*this, m_object.find(key));
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an object
-	 * @param key the property key
-	 * @return the iterator or past the end if not found
-	 */
-	inline const_iterator find(const std::string &key) const
-	{
-		assert(isObject());
-
-		return const_iterator(*this, m_object.find(key));
-	}
-
-	/**
-	 * Insert a new value.
-	 *
-	 * @pre must be an object
-	 * @param name the key
-	 * @param value the value
-	 */
-	inline void insert(std::string name, const Value &value)
-	{
-		assert(isObject());
-
-		m_object.insert({std::move(name), value});
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @pre must be an object
-	 * @param name the key
-	 * @param value the value
-	 */
-	inline void insert(std::string name, Value &&value)
-	{
-		assert(isObject());
-
-		m_object.insert({std::move(name), std::move(value)});
-	}
-
-	/**
-	 * Check if a value exists.
-	 *
-	 * @pre must be an object
-	 * @param key the key value
-	 * @return true if exists
-	 */
-	inline bool contains(const std::string &key) const noexcept
-	{
-		assert(isObject());
-
-		return m_object.find(key) != m_object.end();
-	}
-
-	/**
-	 * Remove a value of the specified key.
-	 *
-	 * @pre must be an object
-	 * @param key the value key
-	 */
-	inline void erase(const std::string &key)
-	{
-		assert(isObject());
-
-		m_object.erase(key);
-	}
-
-	/**
-	 * Return this value as JSon representation.
-	 *
-	 * @param indent, the indentation to use (0 == compact, < 0 == tabs, > 0 == number of spaces)
-	 * @param tabs, use tabs or not
-	 * @return the string
-	 */
-	inline std::string toJson(int indent = 2) const
-	{
-		return toJson(indent, 0);
-	}
-};
-
-/**
- * Escape the input.
- *
- * @param input the input
- * @return the escaped string
- */
-std::string escape(const std::string &input);
-
-/**
- * Convenient function for creating array from initializer list.
- *
- * @param values the values
- * @return the array
- */
-inline Value array(std::initializer_list<Value> values)
-{
-	return Value(std::vector<Value>(values.begin(), values.end()));
-}
-
-/**
- * Convenient function for creating object from initializer list.
- *
- * @param values the values
- * @return the object
- */
-inline Value object(std::initializer_list<std::pair<std::string, Value>> values)
-{
-	return Value(std::map<std::string, Value>(values.begin(), values.end()));
-}
-
-} // !json
-
-} // !irccd
-
-#endif // !_JSON_H_
--- a/common/logger.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,259 +0,0 @@
-/*
- * logger.cpp -- irccd logging
- *
- * 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 <iostream>
-#include <stdexcept>
-#include <streambuf>
-
-#include <irccd-config.h>
-
-#if defined(HAVE_SYSLOG)
-#  include <syslog.h>
-#endif // !HAVE_SYSLOG
-
-#include "logger.h"
-#include "system.h"
-
-namespace irccd {
-
-namespace log {
-
-/* --------------------------------------------------------
- * Buffer -- output buffer (private)
- * -------------------------------------------------------- */
-
-/**
- * @class LoggerBuffer
- * @brief This class is a internal buffer for the Logger streams
- */
-class Buffer : public std::stringbuf {
-private:
-	Interface *m_interface{nullptr};
-	Level m_level;
-
-public:
-	/**
-	 * Create the buffer with the specified level.
-	 *
-	 * @param level the level
-	 */
-	inline Buffer(Level level) noexcept
-		: m_level(level)
-	{
-	}
-
-	/**
-	 * Update the underlying interface.
-	 *
-	 * @param iface is a non-owning pointer to the new interface
-	 */
-	inline void setInterface(Interface *iface) noexcept
-	{
-		m_interface = iface;
-	}
-
-	/**
-	 * Sync the buffer by calling the interface if set.
-	 *
-	 * This function split the buffer line per line and remove it before
-	 * calling the appropriate interface function.
-	 */
-	virtual int sync() override
-	{
-#if defined(NDEBUG)
-		/*
-		 * Debug is disabled, don't call interface->write() but don't
-		 * forget to flush the buffer.
-		 */
-		if (m_level == Level::Debug) {
-			str("");
-
-			return 0;
-		}
-#endif
-
-		/* Verbose is disabled? Don't show and flush the buffer too. */
-		if (m_level == Level::Info && !isVerbose()) {
-			str("");
-
-			return 0;
-		}
-
-		std::string buffer = str();
-		std::string::size_type pos;
-
-		while ((pos = buffer.find("\n")) != std::string::npos) {
-			std::string line = buffer.substr(0, pos);
-
-			/* Remove this line */
-			buffer.erase(buffer.begin(), buffer.begin() + pos + 1);
-
-			if (m_interface)
-				m_interface->write(m_level, line);
-		}
-
-		str(buffer);
-
-		return 0;
-	}
-};
-
-/* --------------------------------------------------------
- * Local variables
- * -------------------------------------------------------- */
-
-namespace {
-
-/* Generic interface for all outputs */
-std::unique_ptr<Interface> iface;
-
-/* Internal buffers */
-Buffer bufferInfo(Level::Info);
-Buffer bufferWarning(Level::Warning);
-Buffer bufferDebug(Level::Debug);
-
-/* Stream outputs */
-std::ostream streamInfo(&bufferInfo);
-std::ostream streamWarning(&bufferWarning);
-std::ostream streamDebug(&bufferDebug);
-
-/* Options */
-bool verbose(false);
-
-} // !namespace
-
-/* --------------------------------------------------------
- * Console
- * -------------------------------------------------------- */
-
-void Console::write(Level level, const std::string &line) noexcept
-{
-	if (level == Level::Warning)
-		std::cerr << line << std::endl;
-	else
-		std::cout << line << std::endl;
-}
-
-/* --------------------------------------------------------
- * File
- * -------------------------------------------------------- */
-
-File::File(std::string normal, std::string errors)
-	: m_outputNormal(std::move(normal))
-	, m_outputError(std::move(errors))
-{
-}
-
-void File::write(Level level, const std::string &line) noexcept
-{
-	std::string &path = (level == Level::Warning) ? m_outputError : m_outputNormal;
-	std::ofstream output(path, std::ofstream::out | std::ofstream::app);
-
-	output << line << std::endl;
-}
-
-/* --------------------------------------------------------
- * Silent
- * -------------------------------------------------------- */
-
-void Silent::write(Level, const std::string &) noexcept
-{
-}
-
-/* --------------------------------------------------------
- * Syslog
- * -------------------------------------------------------- */
-
-#if defined(HAVE_SYSLOG)
-
-Syslog::Syslog()
-{
-	openlog(sys::programName().c_str(), LOG_PID, LOG_DAEMON);
-}
-
-Syslog::~Syslog()
-{
-	closelog();
-}
-
-void Syslog::write(Level level, const std::string &line) noexcept
-{
-	int syslogLevel;
-
-	switch (level) {
-	case Level::Warning:
-		syslogLevel = LOG_WARNING;
-		break;
-	case Level::Debug:
-		syslogLevel = LOG_DEBUG;
-		break;
-	case Level::Info:
-		/* FALLTHROUGH */
-	default:
-		syslogLevel = LOG_INFO;
-		break;
-	}
-
-	syslog(syslogLevel | LOG_USER, "%s", line.c_str());
-}
-
-#endif // !HAVE_SYSLOG
-
-/* --------------------------------------------------------
- * Functions
- * -------------------------------------------------------- */
-
-void setInterface(std::unique_ptr<Interface> ifaceValue) noexcept
-{
-	iface = std::move(ifaceValue);
-	bufferInfo.setInterface(iface.get());
-	bufferWarning.setInterface(iface.get());
-	bufferDebug.setInterface(iface.get());
-}
-
-std::ostream &info() noexcept
-{
-	return streamInfo;
-}
-
-std::ostream &warning() noexcept
-{
-	return streamWarning;
-}
-
-std::ostream &debug() noexcept
-{
-	return streamDebug;
-}
-
-bool isVerbose() noexcept
-{
-	return verbose;
-}
-
-void setVerbose(bool mode) noexcept
-{
-	verbose = mode;
-}
-
-} // !log
-
-} // !irccd
--- a/common/logger.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-/*
- * logger.h -- irccd logging
- *
- * 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_LOGGER_H_
-#define _IRCCD_LOGGER_H_
-
-#include <irccd-config.h>
-
-#include <memory>
-#include <sstream>
-#include <utility>
-
-namespace irccd {
-
-namespace log {
-
-/**
- * @enum Level
- * @brief Which level of warning
- */
-enum class Level {
-	Info,			//!< Standard information (disabled if verbose is false)
-	Warning,		//!< Warning (always shown)
-	Debug			//!< Debug message (only if compiled in debug mode)
-};
-
-/* --------------------------------------------------------
- * Interface -- abstract logging interface
- * -------------------------------------------------------- */
-
-/**
- * @class Interface
- * @brief Interface to implement new logger mechanisms
- *
- * Derive from this class and use Logger::setInterface() to change logging
- * system.
- *
- * @see File
- * @see Console
- * @see Syslog
- * @see Silent
- */
-class Interface {
-public:
-	/**
-	 * Write the line to the logs. The line to write will never contains
-	 * trailing new line character.
-	 *
-	 * @param level the level
-	 * @param line the line without trailing \n
-	 */	
-	virtual void write(Level level, const std::string &line) noexcept = 0;
-};
-
-/* --------------------------------------------------------
- * Console -- logs to console
- * -------------------------------------------------------- */
-
-/**
- * @class Console
- * @brief Logger implementation for console output
- */
-class Console : public Interface {
-public:
-	/**
-	 * @copydoc Interface::write
-	 */
-	void write(Level level, const std::string &line) noexcept override;
-};
-
-/* --------------------------------------------------------
- * File -- logs to a file
- * -------------------------------------------------------- */
-
-/**
- * @class File
- * @brief Output to a file
- */
-class File : public Interface {
-private:
-	std::string m_outputNormal;
-	std::string m_outputError;
-
-public:
-	/**
-	 * Outputs to files. Info and Debug are written in normal and Warnings
-	 * in errors.
-	 *
-	 * The same path can be used for all levels.
-	 *
-	 * @param normal the path to the normal logs
-	 * @param errors the path to the errors logs
-	 */
-	File(std::string normal, std::string errors);
-
-	/**
-	 * @copydoc Interface::write
-	 */
-	void write(Level level, const std::string &line) noexcept override;
-};
-
-/* --------------------------------------------------------
- * Silent -- disable all logs
- * -------------------------------------------------------- */
-
-/**
- * @class Silent
- * @brief Use to disable logs
- *
- * Useful for unit tests when some classes may emits log.
- */
-class Silent : public Interface {
-public:
-	/**
-	 * @copydoc Interface::write
-	 */
-	void write(Level level, const std::string &line) noexcept override;
-};
-
-/* --------------------------------------------------------
- * Syslog -- system logger
- * -------------------------------------------------------- */
-
-#if defined(HAVE_SYSLOG)
-
-/**
- * @class Syslog
- * @brief Implements logger into syslog
- */
-class Syslog : public Interface {
-public:
-	/**
-	 * Open the syslog.
-	 */
-	Syslog();
-
-	/**
-	 * Close the syslog.
-	 */
-	~Syslog();
-
-	/**
-	 * @copydoc Interface::write
-	 */
-	void write(Level level, const std::string &line) noexcept override;
-};
-
-#endif // !HAVE_SYSLOG
-
-/* --------------------------------------------------------
- * Functions
- * -------------------------------------------------------- */
-
-/**
- * Update the logger interface.
- *
- * @param iface the new interface
- */
-void setInterface(std::unique_ptr<Interface> iface) noexcept;
-
-/**
- * Get the stream for informational messages.
- *
- * @return the stream
- * @note Has no effect if verbose is set to false.
- */
-std::ostream &info() noexcept;
-
-/**
- * Get the stream for warnings.
- *
- * @return the stream
- */
-std::ostream &warning() noexcept;
-
-/**
- * Get the stream for debug messages.
- *
- * @return the stream
- * @note Has no effect if compiled in release mode.
- */
-std::ostream &debug() noexcept;
-
-/**
- * Tells if verbose is enabled.
- *
- * @return true if enabled
- */
-bool isVerbose() noexcept;
-
-/**
- * Set the verbosity mode.
- *
- * @param mode the new mode
- */
-void setVerbose(bool mode) noexcept;
-
-} // !log
-
-} // !irccd
-
-#endif // !_IRCCD_LOGGER_H_
--- a/common/options.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * options.cpp -- parse Unix command line options
- *
- * Copyright (c) 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 <cassert>
-
-#include "options.h"
-
-namespace irccd {
-
-namespace parser {
-
-namespace {
-
-using Iterator = std::vector<std::string>::iterator;
-using Args = std::vector<std::string>;
-
-inline bool isOption(const std::string &arg) noexcept
-{
-	return arg.size() >= 2 && arg[0] == '-';
-}
-
-inline bool isLongOption(const std::string &arg) noexcept
-{
-	assert(isOption(arg));
-
-	return arg.size() >= 3 && arg[1] == '-';
-}
-
-inline bool isShortSimple(const std::string &arg) noexcept
-{
-	assert(isOption(arg));
-	assert(!isLongOption(arg));
-
-	return arg.size() == 2;
-}
-
-void parseLongOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
-{
-	auto arg = *it++;
-	auto opt = definition.find(arg);
-
-	if (opt == definition.end())
-		throw InvalidOption(arg);
-
-	/* Need argument? */
-	if (opt->second) {
-		if (it == end || isOption(*it))
-			throw MissingValue(arg);
-
-		result.insert(std::make_pair(arg, *it++));
-		it = args.erase(args.begin(), it);
-		end = args.end();
-	} else {
-		result.insert(std::make_pair(arg, ""));
-		it = args.erase(args.begin());
-		end = args.end();
-	}
-}
-
-void parseShortOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
-{
-	if (isShortSimple(*it)) {
-		/*
-		 * Here two cases:
-		 *
-		 * -v (no option)
-		 * -c value
-		 */
-		auto arg = *it++;
-		auto opt = definition.find(arg);
-
-		if (opt == definition.end())
-			throw InvalidOption(arg);
-
-		/* Need argument? */
-		if (opt->second) {
-			if (it == end || isOption(*it))
-				throw MissingValue(arg);
-
-			result.insert(std::make_pair(arg, *it++));
-			it = args.erase(args.begin(), it);
-			end = args.end();
-		} else {
-			result.insert(std::make_pair(arg, ""));
-			it = args.erase(args.begin());
-			end = args.end();
-		}
-	} else {
-		/*
-		 * Here multiple scenarios:
-		 *
-		 * 1. -abc (-a -b -c if all are simple boolean arguments)
-		 * 2. -vc foo.conf (-v -c foo.conf if -c is argument dependant)
-		 * 3. -vcfoo.conf (-v -c foo.conf also)
-		 */
-		auto value = it->substr(1);
-		auto len = value.length();
-		int toremove = 1;
-
-		for (decltype(len) i = 0; i < len; ++i) {
-			auto arg = std::string{'-'} + value[i];
-			auto opt = definition.find(arg);
-
-			if (opt == definition.end())
-				throw InvalidOption(arg);
-
-			if (opt->second) {
-				if (i == (len - 1)) {
-					/* End of string, get the next argument (see 2.) */
-					if (++it == end || isOption(*it))
-						throw MissingValue(arg);
-
-					result.insert(std::make_pair(arg, *it));
-					toremove += 1;
-				} else {
-					result.insert(std::make_pair(arg, value.substr(i + 1)));
-					i = len;
-				}
-			} else {
-				result.insert(std::make_pair(arg, ""));
-			}
-		}
-
-		it = args.erase(args.begin(), args.begin() + toremove);
-		end = args.end();
-	}
-}
-
-} // !namespace
-
-Result read(std::vector<std::string> &args, const Options &definition)
-{
-	Result result;
-
-	auto it = args.begin();
-	auto end = args.end();
-
-	while (it != end) {
-		if (!isOption(*it))
-			break;
-
-		if (isLongOption(*it))
-			parseLongOption(result, args, it, end, definition);
-		else
-			parseShortOption(result, args, it, end, definition);
-	}
-
-	return result;
-}
-
-Result read(int &argc, char **&argv, const Options &definition)
-{
-	std::vector<std::string> args;
-
-	for (int i = 0; i < argc; ++i)
-		args.push_back(argv[i]);
-
-	auto before = args.size();
-	auto result = read(args, definition);
-
-	argc -= before - args.size();
-	argv += before - args.size();
-
-	return result;
-}
-
-} // !parser
-
-} // !irccd
--- a/common/options.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * options.h -- parse Unix command line options
- *
- * Copyright (c) 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 _OPTION_PARSER_H_
-#define _OPTION_PARSER_H_
-
-#include <exception>
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-namespace irccd {
-
-/**
- * Namespace for options parsing.
- */
-namespace parser {
-
-/**
- * @class InvalidOption
- * @brief This exception is thrown when an invalid option has been found.
- */
-class InvalidOption : public std::exception {
-private:
-	std::string message;
-
-public:
-	/**
-	 * The invalid option given.
-	 */
-	std::string argument;
-
-	/**
-	 * Construct the exception.
-	 *
-	 * @param arg the argument missing
-	 */
-	inline InvalidOption(std::string arg)
-		: argument(std::move(arg))
-	{
-		message = std::string("invalid option: ") + argument;
-	}
-
-	/**
-	 * Get the error message.
-	 *
-	 * @return the error message
-	 */
-	const char *what() const noexcept override
-	{
-		return message.c_str();
-	}
-};
-
-/**
- * @class MissingValue
- * @brief This exception is thrown when an option requires a value and no value has been given
- */
-class MissingValue : public std::exception {
-private:
-	std::string message;
-
-public:
-	/**
-	 * The argument that requires a value.
-	 */
-	std::string argument;
-
-	/**
-	 * Construct the exception.
-	 *
-	 * @param arg the argument that requires a value
-	 */
-	inline MissingValue(std::string arg)
-		: argument(std::move(arg))
-	{
-		message = std::string("missing argument for: ") + argument;
-	}
-
-	/**
-	 * Get the error message.
-	 *
-	 * @return the error message
-	 */
-	const char *what() const noexcept override
-	{
-		return message.c_str();
-	}
-};
-
-/**
- * Packed multimap of options.
- */
-using Result = std::multimap<std::string, std::string>;
-
-/**
- * Define the allowed options.
- */
-using Options = std::map<std::string, bool>;
-
-/**
- * Extract the command line options and return a result.
- *
- * @param args the arguments
- * @param definition
- * @warning the arguments vector is modified in place to remove parsed options
- * @throw MissingValue
- * @throw InvalidOption
- */
-Result read(std::vector<std::string> &args, const Options &definition);
-
-/**
- * Overloaded function for usage with main() arguments.
- *
- * @param argc the number of arguments
- * @param argv the argument vector
- * @param definition
- * @note don't forget to remove the first argv[0] argument
- * @warning the argc and argv are modified in place to remove parsed options
- * @throw MissingValue
- * @throw InvalidOption
- */
-Result read(int &argc, char **&argv, const Options &definition);
-
-} // !parser
-
-} // !irccd
-
-#endif // !_OPTION_PARSER_H_
--- a/common/path.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,554 +0,0 @@
-/*
- * path.cpp -- special paths inside 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 <algorithm>
-#include <cassert>
-#include <sstream>
-#include <stdexcept>
-
-#include <irccd-config.h>
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-#  include <Windows.h>
-#  include <Shlobj.h>
-#else
-#  if defined(IRCCD_SYSTEM_LINUX)
-#    include <limits.h>
-#    include <unistd.h>
-#    include <cerrno>
-#    include <cstring>
-#    include <stdexcept>
-#  endif
-
-#  if defined(IRCCD_SYSTEM_FREEBSD)
-#    include <sys/types.h>
-#    include <sys/sysctl.h>
-#    include <limits.h>
-
-#    include <array>
-#    include <cerrno>
-#    include <cstring>
-#    include <stdexcept>
-#  endif
-
-#  if defined(IRCCD_SYSTEM_MAC)
-#    include <cerrno>
-#    include <cstring>
-#    include <unistd.h>
-#    include <libproc.h>
-#  endif
-
-#  include <xdg.h>
-#endif
-
-#include "filesystem.h"
-#include "path.h"
-#include "system.h"
-#include "util.h"
-
-namespace irccd {
-
-namespace path {
-
-namespace {
-
-/*
- * Base program directory
- * ------------------------------------------------------------------
- *
- * This variable stores the program base directory. It is only enabled when irccd is relocatable because we can
- * retrieve the base directory by removing WITH_BINDIR.
- *
- * If it is empty, the program was not able to detect it (e.g. error, not supported).
- */
-
-#if defined(IRCCD_RELOCATABLE)
-
-std::string base;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-
-std::string executablePath()
-{
-	std::string result;
-	std::size_t size = PATH_MAX;
-	
-	result.resize(size);
-	
-	if (!(size = GetModuleFileNameA(nullptr, &result[0], size)))
-		throw std::runtime_error("GetModuleFileName error");
-	
-	result.resize(size);
-	
-	return result;
-}
-
-#elif defined(IRCCD_SYSTEM_LINUX)
-
-std::string executablePath()
-{
-	std::string result;
-	
-	result.resize(2048);
-	
-	auto size = readlink("/proc/self/exe", &result[0], 2048);
-	
-	if (size < 0)
-		throw std::invalid_argument(std::strerror(errno));
-	
-	result.resize(size);
-	
-	return result;
-}
-
-#elif defined(IRCCD_SYSTEM_FREEBSD)
-
-std::string executablePath()
-{
-	std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } };
-	std::string result;
-	std::size_t size = PATH_MAX + 1;
-	
-	result.resize(size);
-	
-	if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0)
-		throw std::runtime_error(std::strerror(errno));
-	
-	result.resize(size);
-	
-	return result;
-}
-
-#elif defined(IRCCD_SYSTEM_MAC)
-
-std::string executablePath()
-{
-	std::string result;
-	std::size_t size = PROC_PIDPATHINFO_MAXSIZE;
-	
-	result.resize(size);
-	
-	if ((size = proc_pidpath(getpid(), &result[0], size)) == 0)
-		throw std::runtime_error(std::strerror(errno));
-	
-	result.resize(size);
-	
-	return result;
-}
-
-#else
-
-/*
- * TODO: add support for more systems here.
- *
- *  - NetBSD
- *  - OpenBSD
- */
-
-std::string executablePath()
-{
-	return "";
-}
-
-#endif
-
-#endif // !IRCCD_RELOCATABLE
-
-/*
- * System paths
- * ------------------------------------------------------------------
- *
- * Compute system paths.
- *
- * Do not call any of these functions if irccd is relocatable and base is unset.
- */
-
-std::string systemConfig()
-{
-#if defined(IRCCD_RELOCATABLE)
-	assert(!base.empty());
-
-	return base + WITH_CONFDIR;
-#else
-	return fs::isAbsolute(WITH_CONFDIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_CONFDIR;
-#endif
-}
-
-std::string systemData()
-{
-#if defined(IRCCD_RELOCATABLE)
-	assert(!base.empty());
-
-	return base + WITH_DATADIR;
-#else
-	return fs::isAbsolute(WITH_DATADIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_DATADIR;
-#endif
-}
-
-std::string systemCache()
-{
-#if defined(IRCCD_RELOCATABLE)
-	assert(!base.empty());
-
-	return base + WITH_CACHEDIR;
-#else
-	return fs::isAbsolute(WITH_CACHEDIR) ? WITH_CACHEDIR : std::string(PREFIX) + fs::Separator + WITH_CACHEDIR;
-#endif
-}
-
-std::string systemPlugins()
-{
-#if defined(IRCCD_RELOCATABLE)
-	assert(!base.empty());
-
-	return base + WITH_PLUGINDIR;
-#else
-	return fs::isAbsolute(WITH_PLUGINDIR) ? WITH_PLUGINDIR : std::string(PREFIX) + fs::Separator + WITH_PLUGINDIR;
-#endif
-}
-
-/*
- * User paths
- * ------------------------------------------------------------------
- *
- * Compute user paths.
- */
-
-/*
- * userConfig
- * ---------------------------------------------------------
- *
- * Get the path directory to the user configuration. Example:
- *
- * Unix:
- *
- * XDG_CONFIG_HOME/irccd
- * HOME/.config/irccd
- *
- * Windows:
- *
- * CSIDL_LOCAL_APPDATA/irccd/config
- */
-std::string userConfig()
-{
-	std::ostringstream oss;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	char path[MAX_PATH];
-
-	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK) {
-		oss << "";
-	} else {
-		oss << path;
-		oss << "\\irccd\\config\\";
-	}
-#else
-	try {
-		Xdg xdg;
-
-		oss << xdg.configHome();
-		oss << "/irccd/";
-	} catch (const std::exception &) {
-		const char *home = getenv("HOME");
-
-		if (home != nullptr)
-			oss << home;
-
-		oss << "/.config/irccd/";
-	}
-#endif
-
-	return oss.str();
-}
-
-/*
- * userData
- * --------------------------------------------------------
- *
- * Get the path to the data application.
- *
- * Unix:
- *
- * XDG_DATA_HOME/irccd
- * HOME/.local/share/irccd
- *
- * Windows:
- *
- * CSIDL_LOCAL_APPDATA
- */
-std::string userData()
-{
-	std::ostringstream oss;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	char path[MAX_PATH];
-
-	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK)
-		oss << "";
-	else {
-		oss << path;
-		oss << "\\irccd\\share";
-	}
-#else
-	try {
-		Xdg xdg;
-
-		oss << xdg.dataHome();
-		oss << "/irccd/";
-	} catch (const std::exception &) {
-		const char *home = getenv("HOME");
-
-		if (home != nullptr)
-			oss << home;
-
-		oss << "/.local/share/irccd/";
-	}
-#endif
-
-	return oss.str();
-}
-
-/*
- * userCache
- * --------------------------------------------------------
- *
- * Directory for cache files.
- *
- * Unix:
- *
- * XDG_CACHE_HOME/irccd
- * HOME/.cache/irccd
- *
- * Windows:
- *
- * %TEMP% (e.g. C:\Users\<user>\AppData\Local\Temp)
- */
-std::string userCache()
-{
-	std::ostringstream oss;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	char path[MAX_PATH + 1];
-
-	GetTempPathA(sizeof (path), path);
-
-	oss << path << "\\irccd\\";
-#else
-	try {
-		Xdg xdg;
-
-		oss << xdg.cacheHome();
-		oss << "/irccd/";
-	} catch (const std::exception &) {
-		const char *home = getenv("HOME");
-
-		if (home != nullptr)
-			oss << home;
-
-		oss << "/.cache/irccd/";
-	}
-#endif
-
-	return oss.str();
-}
-
-/*
- * userPlugins
- * --------------------------------------------------------
- *
- * Path to the data + plugins.
- */
-std::string userPlugins()
-{
-	return userData() + "/plugins/";
-}
-
-} // !namespace
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-const char Separator(';');
-#else
-const char Separator(':');
-#endif
-
-void setApplicationPath(const std::string &argv0)
-{
-#if defined(IRCCD_RELOCATABLE)
-	try {
-		base = executablePath();
-	} catch (const std::exception &) {
-		/*
-		 * If an exception is thrown, that means the operatin system supports a function to get the executable
-		 * path but it failed.
-		 *
-		 * TODO: show a waning
-		 */
-	}
-
-	/*
-	 * If we could not get the application path from the native function, check if argv[0] is an absolute path
-	 * and use that from there.
-	 *
-	 * Otherwise, search from the PATH.
-	 *
-	 * In the worst case use current working directory.
-	 */
-	if (base.empty()) {
-		if (fs::isAbsolute(argv0)) {
-			base = argv0;
-		} else {
-			std::string name = fs::baseName(argv0);
-
-			for (const auto &dir : util::split(sys::env("PATH"), std::string(1, Separator))) {
-				std::string path = dir + fs::Separator + name;
-
-				if (fs::exists(path)) {
-					base = path;
-					break;
-				}
-			}
-
-			/* Not found in PATH? add dummy value */
-			if (base.empty())
-				base = std::string(".") + fs::Separator + WITH_BINDIR + fs::Separator + "dummy";
-		}
-	}
-
-	/* Find bin/<progname> */
-	auto pos = base.rfind(std::string(WITH_BINDIR) + fs::Separator + fs::baseName(base));
-
-	if (pos != std::string::npos)
-		base.erase(pos);
-
-	/* Add trailing / or \\ for convenience */
-	base = clean(base);
-
-	assert(!base.empty());
-#else
-	(void)argv0;
-#endif
-}
-
-std::string clean(std::string input)
-{
-	if (input.empty())
-		return input;
-
-	/* First, remove any duplicates */
-	input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) {
-		return c1 == c2 && (c1 == '/' || c1 == '\\');
-	}), input.end());
-
-	/* Add a trailing / or \\ */
-	char c = input[input.length() - 1];
-	if (c != '/' && c != '\\')
-		input += fs::Separator;
-
-	/* Now converts all / to \\ for Windows and the opposite for Unix */
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	std::replace(input.begin(), input.end(), '/', '\\');
-#else
-	std::replace(input.begin(), input.end(), '\\', '/');
-#endif
-
-	return input;
-}
-
-std::string get(Path path, Owner owner)
-{
-	assert(path >= PathConfig && path <= PathPlugins);
-	assert(owner >= OwnerSystem && owner <= OwnerUser);
-
-	std::string result;
-
-	switch (owner) {
-	case OwnerSystem:
-		switch (path) {
-		case PathCache:
-			result = clean(systemCache());
-			break;
-		case PathConfig:
-			result = clean(systemConfig());
-			break;
-		case PathData:
-			result = clean(systemData());
-			break;
-		case PathPlugins:
-			result = clean(systemPlugins());
-			break;
-		default:
-			break;
-		}
-	case OwnerUser:
-		switch (path) {
-		case PathCache:
-			result = clean(userCache());
-			break;
-		case PathConfig:
-			result = clean(userConfig());
-			break;
-		case PathData:
-			result = clean(userData());
-			break;
-		case PathPlugins:
-			result = clean(userPlugins());
-			break;
-		default:
-			break;
-		}
-	default:
-		break;
-	}
-
-	return result;
-}
-
-std::vector<std::string> list(Path path)
-{
-	assert(path >= PathConfig && path <= PathPlugins);
-
-	std::vector<std::string> list;
-
-	switch (path) {
-	case PathCache:
-		list.push_back(clean(userCache()));
-		list.push_back(clean(systemCache()));
-		break;
-	case PathConfig:
-		list.push_back(clean(userConfig()));
-		list.push_back(clean(systemConfig()));
-		break;
-	case PathData:
-		list.push_back(clean(userData()));
-		list.push_back(clean(systemData()));
-		break;
-	case PathPlugins:
-		list.push_back(clean(fs::cwd()));
-		list.push_back(clean(userPlugins()));
-		list.push_back(clean(systemPlugins()));
-		break;
-	default:
-		break;
-	}
-
-	return list;
-}
-
-} // !path
-
-} // !irccd
--- a/common/path.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * path.h -- special paths inside 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_PATH_H_
-#define _IRCCD_PATH_H_
-
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-namespace path {
-
-/**
- * PATH separator, either : or ;.
- */
-extern const char Separator;
-
-/**
- * @enum Path
- * @brief Which special path to get
- */
-enum Path {
-	PathConfig,			//!< Configuration files
-	PathData,			//!< Data directory
-	PathCache,			//!< Cache files
-	PathPlugins			//!< Path to the plugins
-};
-
-/**
- * @enum Owner
- * @brief For paths, get the installation path or the user ones
- */
-enum Owner {
-	OwnerSystem,			//!< System wide
-	OwnerUser			//!< User
-};
-
-/**
- * This function must be called before at the beginning of the main.
- *
- * It use system dependant program path lookup if available and fallbacks to the path given as argument if any failure
- * was encoutered.
- *
- * @param argv0 the path to the executable (argv[0])
- */
-void setApplicationPath(const std::string &argv0);
-
-/**
- * Clean a path by removing any extra / or \ and add a trailing one.
- *
- * @param path the path
- * @return the updated path
- */
-std::string clean(std::string path);
-
-/**
- * Generic function for path retrievement.
- *
- * The path is always terminated by a trailing / or \\.
- *
- * @pre setApplicationPath must have been called
- * @param path the type of path
- * @param owner system or user wide
- * @return the path
- */
-std::string get(Path path, Owner owner);
-
-/**
- * Generic function for multiple paths.
- *
- * This function will add more directories than pathSystem*() and pathUser*() functions, for example
- * it will add some path if irccd is relocatable.
- *
- * @pre setApplicationPath must have been called
- * @param path the type of path
- * @return the list of preferred directories in order
- */
-std::vector<std::string> list(Path path);
-
-} // !path
-
-} // !irccd
-
-#endif // !_IRCCD_PATH_H_
--- a/common/signals.h	Mon Mar 14 11:24:25 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/common/sockets.cpp	Mon Mar 14 11:24:25 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/common/sockets.h	Mon Mar 14 11:24:25 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() : SSLv3_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/common/system.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/*
- * system.cpp -- platform dependent functions for system inspection
- *
- * 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 <cstdlib>
-#include <ctime>
-#include <stdexcept>
-
-#include <irccd-config.h>
-
-#if defined(HAVE_SETPROGNAME)
-#  include <cstdlib>
-#endif
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-#  include <sys/types.h>
-#  include <sys/timeb.h>
-#  include <Windows.h>
-#  include <Shlobj.h>
-#else // All non Windows
-#if defined(IRCCD_SYSTEM_MAC)
-#  include <sys/sysctl.h>
-#endif
-
-#if defined(IRCCD_SYSTEM_LINUX)
-#  include <sys/sysinfo.h>
-#endif
-
-#  include <sys/utsname.h>
-#  include <sys/time.h>
-#  include <sys/types.h>
-
-#  include <unistd.h>
-
-#  include <cerrno>
-#  include <cstring>
-#  include <stdexcept>
-#  include <ctime>
-
-#endif
-
-/* For sys::setGid */
-#if defined(HAVE_SETGID)
-#  include <sys/types.h>
-#  include <unistd.h>
-#  include <grp.h>
-#endif
-
-/* For sys::setUid */
-#if defined(HAVE_SETGID)
-#  include <sys/types.h>
-#  include <unistd.h>
-#  include <pwd.h>
-#endif
-
-#include "filesystem.h"
-#include "logger.h"
-#include "system.h"
-#include "util.h"
-
-namespace irccd {
-
-namespace sys {
-
-namespace {
-
-/*
- * setHelper
- * ------------------------------------------------------------------
- *
- * This is an helper for setting the uid or gid. It accepts both numeric and string uid and gid.
- *
- * If a name is specified as uid/group, the lookup function will be called and must be getpwname or
- * getgrname. Then, to get the id from the returned structure (struct passwd, struct group), the getter
- * function will return either pw_uid or gr_gid.
- *
- * Finally, when the id is resolved, the setter function (setuid, setgid) will be called.
- *
- * @param typeName the type of id (uid or gid)
- * @param value the value (numeric or name)
- * @param lookup the lookup function to resolve the name (getpwnam or getgrnam)
- * @param setter the function to apply the id (setuid or setgid)
- * @param getter the function to get the id from the informal structure
- */
-template <typename IntType, typename LookupFunc, typename SetterFunc, typename FieldGetter>
-void setHelper(const std::string &typeName, const std::string &value, LookupFunc lookup, SetterFunc setter, FieldGetter getter)
-{
-	IntType id;
-
-	if (util::isNumber(value)) {
-		id = std::stoi(value);
-	} else {
-		auto info = lookup(value.c_str());
-
-		if (info == nullptr) {
-			log::warning() << "irccd: invalid " << typeName << ": " << std::strerror(errno) << std::endl;
-			return;
-		} else {
-			id = getter(info);
-
-			log::debug() << "irccd: " << typeName << " " << value << " resolved to: " << id << std::endl;
-		}
-	}
-
-	if (setter(id) < 0)
-		log::warning() << "irccd: could not set " << typeName << ": " << std::strerror(errno) << std::endl;
-	else
-		log::info() << "irccd: setting " << typeName << " to " << value << std::endl;
-}
-
-/*
- * XXX: the setprogname() function keeps a pointer without copying it so when main's argv is modified, we're not using
- * the same name so create our own copy.
- */
-
-std::string programNameCopy;
-
-} // !namespace
-
-void setProgramName(std::string name) noexcept
-{
-	programNameCopy = std::move(name);
-
-#if defined(HAVE_SETPROGNAME)
-	setprogname(programNameCopy.c_str());
-#endif
-}
-
-const std::string &programName() noexcept
-{
-	return programNameCopy;
-}
-
-std::string name()
-{
-#if defined(IRCCD_SYSTEM_LINUX)
-	return "Linux";
-#elif defined(IRCCD_SYSTEM_WINDOWS)
-	return "Windows";
-#elif defined(IRCCD_SYSTEM_FREEBSD)
-	return "FreeBSD";
-#elif defined(IRCCD_SYSTEM_OPENBSD)
-	return "OpenBSD";
-#elif defined(IRCCD_SYSTEM_NETBSD)
-	return "NetBSD";
-#elif defined(IRCCD_SYSTEM_MAC)
-	return "Mac";
-#else
-	return "Unknown";
-#endif
-}
-
-std::string version()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	auto version = GetVersion();
-	auto major = (DWORD)(LOBYTE(LOWORD(version)));
-	auto minor = (DWORD)(HIBYTE(LOWORD(version)));
-
-	return std::to_string(major) + "." + std::to_string(minor);
-#else
-	struct utsname uts;
-
-	if (uname(&uts) < 0)
-		throw std::runtime_error(std::strerror(errno));
-
-	return std::string(uts.release);
-#endif
-}
-
-uint64_t uptime()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	return ::GetTickCount64() / 1000;
-#elif defined(IRCCD_SYSTEM_LINUX)
-	struct sysinfo info;
-
-	if (sysinfo(&info) < 0)
-		throw std::runtime_error(std::strerror(errno));
-
-	return info.uptime;
-#elif defined(IRCCD_SYSTEM_MAC)
-	struct timeval boottime;
-	size_t length = sizeof (boottime);
-	int mib[2] = { CTL_KERN, KERN_BOOTTIME };
-
-	if (sysctl(mib, 2, &boottime, &length, nullptr, 0) < 0)
-		throw std::runtime_error(std::strerror(errno));
-
-	time_t bsec = boottime.tv_sec, csec = time(nullptr);
-
-	return difftime(csec, bsec);
-#else
-	/* BSD */
-	struct timespec ts;
-
-	if (clock_gettime(CLOCK_UPTIME, &ts) < 0)
-		throw std::runtime_error(std::strerror(errno));
-
-	return ts.tv_sec;
-#endif
-}
-
-uint64_t ticks()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	_timeb tp;
-
-	_ftime(&tp);
-
-	return tp.time * 1000LL + tp.millitm;
-#else
-	struct timeval tp;
-
-	gettimeofday(&tp, NULL);
-
-	return tp.tv_sec * 1000LL + tp.tv_usec / 1000;
-#endif
-}
-
-std::string home()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	char path[MAX_PATH];
-
-	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK)
-		return "";
-
-	return std::string(path);
-#else
-	return env("HOME");
-#endif
-}
-
-std::string env(const std::string &var)
-{
-	auto value = std::getenv(var.c_str());
-
-	if (value == nullptr)
-		return "";
-
-	return value;
-}
-
-#if defined(HAVE_SETUID)
-
-void setUid(const std::string &value)
-{
-	setHelper<uid_t>("uid", value, &getpwnam, &setuid, [] (const struct passwd *pw) {
-		return pw->pw_uid;
-	});
-}
-
-#endif
-
-#if defined(HAVE_SETGID)
-
-void setGid(const std::string &value)
-{
-	setHelper<gid_t>("gid", value, &getgrnam, &setgid, [] (const struct group *gr) {
-		return gr->gr_gid;
-	});
-}
-
-#endif
-
-} // !sys
-
-} // !irccd
--- a/common/system.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * system.h -- platform dependent functions for system inspection
- *
- * 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_SYSTEM_H_
-#define _IRCCD_SYSTEM_H_
-
-/**
- * @file system.h
- * @brief System dependant functions
- */
-
-#include <irccd-config.h>
-
-#include <cstdint>
-#include <string>
-
-namespace irccd {
-
-namespace sys {
-
-/**
- * Set the program name, needed for some functions or some systems.
- *
- * @param name the program name
- */
-void setProgramName(std::string name) noexcept;
-
-/**
- * Get the program name.
- *
- * @return the program name
- */
-const std::string &programName() noexcept;
-
-/**
- * Get the system name.
- *
- * @return the name
- */
-std::string name();
-
-/**
- * Get the system version.
- *
- * @return the version
- */
-std::string version();
-
-/**
- * Get the number of seconds elapsed since the boottime.
- *
- * @return the number of seconds
- */
-uint64_t uptime();
-
-/**
- * Get the milliseconds elapsed since the application
- * startup.
- *
- * @return the milliseconds
- */
-uint64_t ticks();
-
-/**
- * Get an environment variable.
- *
- * @return the value or empty string
- */
-std::string env(const std::string &var);
-
-/**
- * Get home directory usually /home/foo
- *
- * @return the home directory
- */
-std::string home();
-
-#if defined(HAVE_SETUID)
-
-/**
- * Set the effective uid by name or numeric value.
- *
- * @param value the value
- */
-void setUid(const std::string &value);
-
-#endif
-
-#if defined(HAVE_SETGID)
-
-/**
- * Set the effective gid by name or numeric value.
- *
- * @param value the value
- */
-void setGid(const std::string &value);
-
-#endif
-
-} // !sys
-
-} // !irccd
-
-#endif // !_IRCCD_SYSTEM_H_
--- a/common/unicode.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4712 +0,0 @@
-/*
- * unicode.cpp -- UTF-8 to UTF-32 conversions and various operations
- *
- * 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 "unicode.h"
-
-/*
- * The following code has been generated from Go mkrunetype adapted to our
- * needs.
- */
-
-namespace irccd {
-
-namespace unicode {
-
-#define nelem(x) (sizeof (x) / sizeof ((x)[0]))
-
-char32_t *rbsearch(char32_t c, char32_t *t, int n, int ne) noexcept
-{
-	char32_t *p;
-	int m;
-
-	while (n > 1) {
-		m = n >> 1;
-		p = t + m * ne;
-
-		if (c >= p[0]) {
-			t = p;
-			n = n - m;
-		} else {
-			n = m;
-		}
-	}
-
-	if (n && c >= t[0])
-		return t;
-
-	return nullptr;
-}
-
-static char32_t isspacer[] = {
-	0x0009, 0x000d,
-	0x0020, 0x0020,
-	0x0085, 0x0085,
-	0x00a0, 0x00a0,
-	0x1680, 0x1680,
-	0x2000, 0x200a,
-	0x2028, 0x2029,
-	0x202f, 0x202f,
-	0x205f, 0x205f,
-	0x3000, 0x3000,
-	0xfeff, 0xfeff,
-};
-
-bool isspace(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, isspacer, nelem (isspacer)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	return false;
-}
-
-static char32_t isdigitr[] = {
-	0x0030, 0x0039,
-	0x0660, 0x0669,
-	0x06f0, 0x06f9,
-	0x07c0, 0x07c9,
-	0x0966, 0x096f,
-	0x09e6, 0x09ef,
-	0x0a66, 0x0a6f,
-	0x0ae6, 0x0aef,
-	0x0b66, 0x0b6f,
-	0x0be6, 0x0bef,
-	0x0c66, 0x0c6f,
-	0x0ce6, 0x0cef,
-	0x0d66, 0x0d6f,
-	0x0de6, 0x0def,
-	0x0e50, 0x0e59,
-	0x0ed0, 0x0ed9,
-	0x0f20, 0x0f29,
-	0x1040, 0x1049,
-	0x1090, 0x1099,
-	0x17e0, 0x17e9,
-	0x1810, 0x1819,
-	0x1946, 0x194f,
-	0x19d0, 0x19d9,
-	0x1a80, 0x1a89,
-	0x1a90, 0x1a99,
-	0x1b50, 0x1b59,
-	0x1bb0, 0x1bb9,
-	0x1c40, 0x1c49,
-	0x1c50, 0x1c59,
-	0xa620, 0xa629,
-	0xa8d0, 0xa8d9,
-	0xa900, 0xa909,
-	0xa9d0, 0xa9d9,
-	0xa9f0, 0xa9f9,
-	0xaa50, 0xaa59,
-	0xabf0, 0xabf9,
-	0xff10, 0xff19,
-	0x104a0, 0x104a9,
-	0x11066, 0x1106f,
-	0x110f0, 0x110f9,
-	0x11136, 0x1113f,
-	0x111d0, 0x111d9,
-	0x112f0, 0x112f9,
-	0x114d0, 0x114d9,
-	0x11650, 0x11659,
-	0x116c0, 0x116c9,
-	0x118e0, 0x118e9,
-	0x16a60, 0x16a69,
-	0x16b50, 0x16b59,
-	0x1d7ce, 0x1d7ff,
-};
-
-bool isdigit(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, isdigitr, nelem (isdigitr)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	return false;
-}
-
-static char32_t isalphar[] = {
-	0x0041, 0x005a,
-	0x0061, 0x007a,
-	0x00c0, 0x00d6,
-	0x00d8, 0x00f6,
-	0x00f8, 0x02c1,
-	0x02c6, 0x02d1,
-	0x02e0, 0x02e4,
-	0x0370, 0x0374,
-	0x0376, 0x0377,
-	0x037a, 0x037d,
-	0x0388, 0x038a,
-	0x038e, 0x03a1,
-	0x03a3, 0x03f5,
-	0x03f7, 0x0481,
-	0x048a, 0x052f,
-	0x0531, 0x0556,
-	0x0561, 0x0587,
-	0x05d0, 0x05ea,
-	0x05f0, 0x05f2,
-	0x0620, 0x064a,
-	0x066e, 0x066f,
-	0x0671, 0x06d3,
-	0x06e5, 0x06e6,
-	0x06ee, 0x06ef,
-	0x06fa, 0x06fc,
-	0x0712, 0x072f,
-	0x074d, 0x07a5,
-	0x07ca, 0x07ea,
-	0x07f4, 0x07f5,
-	0x0800, 0x0815,
-	0x0840, 0x0858,
-	0x08a0, 0x08b2,
-	0x0904, 0x0939,
-	0x0958, 0x0961,
-	0x0971, 0x0980,
-	0x0985, 0x098c,
-	0x098f, 0x0990,
-	0x0993, 0x09a8,
-	0x09aa, 0x09b0,
-	0x09b6, 0x09b9,
-	0x09dc, 0x09dd,
-	0x09df, 0x09e1,
-	0x09f0, 0x09f1,
-	0x0a05, 0x0a0a,
-	0x0a0f, 0x0a10,
-	0x0a13, 0x0a28,
-	0x0a2a, 0x0a30,
-	0x0a32, 0x0a33,
-	0x0a35, 0x0a36,
-	0x0a38, 0x0a39,
-	0x0a59, 0x0a5c,
-	0x0a72, 0x0a74,
-	0x0a85, 0x0a8d,
-	0x0a8f, 0x0a91,
-	0x0a93, 0x0aa8,
-	0x0aaa, 0x0ab0,
-	0x0ab2, 0x0ab3,
-	0x0ab5, 0x0ab9,
-	0x0ae0, 0x0ae1,
-	0x0b05, 0x0b0c,
-	0x0b0f, 0x0b10,
-	0x0b13, 0x0b28,
-	0x0b2a, 0x0b30,
-	0x0b32, 0x0b33,
-	0x0b35, 0x0b39,
-	0x0b5c, 0x0b5d,
-	0x0b5f, 0x0b61,
-	0x0b85, 0x0b8a,
-	0x0b8e, 0x0b90,
-	0x0b92, 0x0b95,
-	0x0b99, 0x0b9a,
-	0x0b9e, 0x0b9f,
-	0x0ba3, 0x0ba4,
-	0x0ba8, 0x0baa,
-	0x0bae, 0x0bb9,
-	0x0c05, 0x0c0c,
-	0x0c0e, 0x0c10,
-	0x0c12, 0x0c28,
-	0x0c2a, 0x0c39,
-	0x0c58, 0x0c59,
-	0x0c60, 0x0c61,
-	0x0c85, 0x0c8c,
-	0x0c8e, 0x0c90,
-	0x0c92, 0x0ca8,
-	0x0caa, 0x0cb3,
-	0x0cb5, 0x0cb9,
-	0x0ce0, 0x0ce1,
-	0x0cf1, 0x0cf2,
-	0x0d05, 0x0d0c,
-	0x0d0e, 0x0d10,
-	0x0d12, 0x0d3a,
-	0x0d60, 0x0d61,
-	0x0d7a, 0x0d7f,
-	0x0d85, 0x0d96,
-	0x0d9a, 0x0db1,
-	0x0db3, 0x0dbb,
-	0x0dc0, 0x0dc6,
-	0x0e01, 0x0e30,
-	0x0e32, 0x0e33,
-	0x0e40, 0x0e46,
-	0x0e81, 0x0e82,
-	0x0e87, 0x0e88,
-	0x0e94, 0x0e97,
-	0x0e99, 0x0e9f,
-	0x0ea1, 0x0ea3,
-	0x0eaa, 0x0eab,
-	0x0ead, 0x0eb0,
-	0x0eb2, 0x0eb3,
-	0x0ec0, 0x0ec4,
-	0x0edc, 0x0edf,
-	0x0f40, 0x0f47,
-	0x0f49, 0x0f6c,
-	0x0f88, 0x0f8c,
-	0x1000, 0x102a,
-	0x1050, 0x1055,
-	0x105a, 0x105d,
-	0x1065, 0x1066,
-	0x106e, 0x1070,
-	0x1075, 0x1081,
-	0x10a0, 0x10c5,
-	0x10d0, 0x10fa,
-	0x10fc, 0x1248,
-	0x124a, 0x124d,
-	0x1250, 0x1256,
-	0x125a, 0x125d,
-	0x1260, 0x1288,
-	0x128a, 0x128d,
-	0x1290, 0x12b0,
-	0x12b2, 0x12b5,
-	0x12b8, 0x12be,
-	0x12c2, 0x12c5,
-	0x12c8, 0x12d6,
-	0x12d8, 0x1310,
-	0x1312, 0x1315,
-	0x1318, 0x135a,
-	0x1380, 0x138f,
-	0x13a0, 0x13f4,
-	0x1401, 0x166c,
-	0x166f, 0x167f,
-	0x1681, 0x169a,
-	0x16a0, 0x16ea,
-	0x16f1, 0x16f8,
-	0x1700, 0x170c,
-	0x170e, 0x1711,
-	0x1720, 0x1731,
-	0x1740, 0x1751,
-	0x1760, 0x176c,
-	0x176e, 0x1770,
-	0x1780, 0x17b3,
-	0x1820, 0x1877,
-	0x1880, 0x18a8,
-	0x18b0, 0x18f5,
-	0x1900, 0x191e,
-	0x1950, 0x196d,
-	0x1970, 0x1974,
-	0x1980, 0x19ab,
-	0x19c1, 0x19c7,
-	0x1a00, 0x1a16,
-	0x1a20, 0x1a54,
-	0x1b05, 0x1b33,
-	0x1b45, 0x1b4b,
-	0x1b83, 0x1ba0,
-	0x1bae, 0x1baf,
-	0x1bba, 0x1be5,
-	0x1c00, 0x1c23,
-	0x1c4d, 0x1c4f,
-	0x1c5a, 0x1c7d,
-	0x1ce9, 0x1cec,
-	0x1cee, 0x1cf1,
-	0x1cf5, 0x1cf6,
-	0x1d00, 0x1dbf,
-	0x1e00, 0x1f15,
-	0x1f18, 0x1f1d,
-	0x1f20, 0x1f45,
-	0x1f48, 0x1f4d,
-	0x1f50, 0x1f57,
-	0x1f5f, 0x1f7d,
-	0x1f80, 0x1fb4,
-	0x1fb6, 0x1fbc,
-	0x1fc2, 0x1fc4,
-	0x1fc6, 0x1fcc,
-	0x1fd0, 0x1fd3,
-	0x1fd6, 0x1fdb,
-	0x1fe0, 0x1fec,
-	0x1ff2, 0x1ff4,
-	0x1ff6, 0x1ffc,
-	0x2090, 0x209c,
-	0x210a, 0x2113,
-	0x2119, 0x211d,
-	0x212a, 0x212d,
-	0x212f, 0x2139,
-	0x213c, 0x213f,
-	0x2145, 0x2149,
-	0x2183, 0x2184,
-	0x2c00, 0x2c2e,
-	0x2c30, 0x2c5e,
-	0x2c60, 0x2ce4,
-	0x2ceb, 0x2cee,
-	0x2cf2, 0x2cf3,
-	0x2d00, 0x2d25,
-	0x2d30, 0x2d67,
-	0x2d80, 0x2d96,
-	0x2da0, 0x2da6,
-	0x2da8, 0x2dae,
-	0x2db0, 0x2db6,
-	0x2db8, 0x2dbe,
-	0x2dc0, 0x2dc6,
-	0x2dc8, 0x2dce,
-	0x2dd0, 0x2dd6,
-	0x2dd8, 0x2dde,
-	0x3005, 0x3006,
-	0x3031, 0x3035,
-	0x303b, 0x303c,
-	0x3041, 0x3096,
-	0x309d, 0x309f,
-	0x30a1, 0x30fa,
-	0x30fc, 0x30ff,
-	0x3105, 0x312d,
-	0x3131, 0x318e,
-	0x31a0, 0x31ba,
-	0x31f0, 0x31ff,
-	0x3400, 0x4db5,
-	0x4e00, 0x9fcc,
-	0xa000, 0xa48c,
-	0xa4d0, 0xa4fd,
-	0xa500, 0xa60c,
-	0xa610, 0xa61f,
-	0xa62a, 0xa62b,
-	0xa640, 0xa66e,
-	0xa67f, 0xa69d,
-	0xa6a0, 0xa6e5,
-	0xa717, 0xa71f,
-	0xa722, 0xa788,
-	0xa78b, 0xa78e,
-	0xa790, 0xa7ad,
-	0xa7b0, 0xa7b1,
-	0xa7f7, 0xa801,
-	0xa803, 0xa805,
-	0xa807, 0xa80a,
-	0xa80c, 0xa822,
-	0xa840, 0xa873,
-	0xa882, 0xa8b3,
-	0xa8f2, 0xa8f7,
-	0xa90a, 0xa925,
-	0xa930, 0xa946,
-	0xa960, 0xa97c,
-	0xa984, 0xa9b2,
-	0xa9e0, 0xa9e4,
-	0xa9e6, 0xa9ef,
-	0xa9fa, 0xa9fe,
-	0xaa00, 0xaa28,
-	0xaa40, 0xaa42,
-	0xaa44, 0xaa4b,
-	0xaa60, 0xaa76,
-	0xaa7e, 0xaaaf,
-	0xaab5, 0xaab6,
-	0xaab9, 0xaabd,
-	0xaadb, 0xaadd,
-	0xaae0, 0xaaea,
-	0xaaf2, 0xaaf4,
-	0xab01, 0xab06,
-	0xab09, 0xab0e,
-	0xab11, 0xab16,
-	0xab20, 0xab26,
-	0xab28, 0xab2e,
-	0xab30, 0xab5a,
-	0xab5c, 0xab5f,
-	0xab64, 0xab65,
-	0xabc0, 0xabe2,
-	0xac00, 0xd7a3,
-	0xd7b0, 0xd7c6,
-	0xd7cb, 0xd7fb,
-	0xf900, 0xfa6d,
-	0xfa70, 0xfad9,
-	0xfb00, 0xfb06,
-	0xfb13, 0xfb17,
-	0xfb1f, 0xfb28,
-	0xfb2a, 0xfb36,
-	0xfb38, 0xfb3c,
-	0xfb40, 0xfb41,
-	0xfb43, 0xfb44,
-	0xfb46, 0xfbb1,
-	0xfbd3, 0xfd3d,
-	0xfd50, 0xfd8f,
-	0xfd92, 0xfdc7,
-	0xfdf0, 0xfdfb,
-	0xfe70, 0xfe74,
-	0xfe76, 0xfefc,
-	0xff21, 0xff3a,
-	0xff41, 0xff5a,
-	0xff66, 0xffbe,
-	0xffc2, 0xffc7,
-	0xffca, 0xffcf,
-	0xffd2, 0xffd7,
-	0xffda, 0xffdc,
-	0x10000, 0x1000b,
-	0x1000d, 0x10026,
-	0x10028, 0x1003a,
-	0x1003c, 0x1003d,
-	0x1003f, 0x1004d,
-	0x10050, 0x1005d,
-	0x10080, 0x100fa,
-	0x10280, 0x1029c,
-	0x102a0, 0x102d0,
-	0x10300, 0x1031f,
-	0x10330, 0x10340,
-	0x10342, 0x10349,
-	0x10350, 0x10375,
-	0x10380, 0x1039d,
-	0x103a0, 0x103c3,
-	0x103c8, 0x103cf,
-	0x10400, 0x1049d,
-	0x10500, 0x10527,
-	0x10530, 0x10563,
-	0x10600, 0x10736,
-	0x10740, 0x10755,
-	0x10760, 0x10767,
-	0x10800, 0x10805,
-	0x1080a, 0x10835,
-	0x10837, 0x10838,
-	0x1083f, 0x10855,
-	0x10860, 0x10876,
-	0x10880, 0x1089e,
-	0x10900, 0x10915,
-	0x10920, 0x10939,
-	0x10980, 0x109b7,
-	0x109be, 0x109bf,
-	0x10a10, 0x10a13,
-	0x10a15, 0x10a17,
-	0x10a19, 0x10a33,
-	0x10a60, 0x10a7c,
-	0x10a80, 0x10a9c,
-	0x10ac0, 0x10ac7,
-	0x10ac9, 0x10ae4,
-	0x10b00, 0x10b35,
-	0x10b40, 0x10b55,
-	0x10b60, 0x10b72,
-	0x10b80, 0x10b91,
-	0x10c00, 0x10c48,
-	0x11003, 0x11037,
-	0x11083, 0x110af,
-	0x110d0, 0x110e8,
-	0x11103, 0x11126,
-	0x11150, 0x11172,
-	0x11183, 0x111b2,
-	0x111c1, 0x111c4,
-	0x11200, 0x11211,
-	0x11213, 0x1122b,
-	0x112b0, 0x112de,
-	0x11305, 0x1130c,
-	0x1130f, 0x11310,
-	0x11313, 0x11328,
-	0x1132a, 0x11330,
-	0x11332, 0x11333,
-	0x11335, 0x11339,
-	0x1135d, 0x11361,
-	0x11480, 0x114af,
-	0x114c4, 0x114c5,
-	0x11580, 0x115ae,
-	0x11600, 0x1162f,
-	0x11680, 0x116aa,
-	0x118a0, 0x118df,
-	0x11ac0, 0x11af8,
-	0x12000, 0x12398,
-	0x13000, 0x1342e,
-	0x16800, 0x16a38,
-	0x16a40, 0x16a5e,
-	0x16ad0, 0x16aed,
-	0x16b00, 0x16b2f,
-	0x16b40, 0x16b43,
-	0x16b63, 0x16b77,
-	0x16b7d, 0x16b8f,
-	0x16f00, 0x16f44,
-	0x16f93, 0x16f9f,
-	0x1b000, 0x1b001,
-	0x1bc00, 0x1bc6a,
-	0x1bc70, 0x1bc7c,
-	0x1bc80, 0x1bc88,
-	0x1bc90, 0x1bc99,
-	0x1d400, 0x1d454,
-	0x1d456, 0x1d49c,
-	0x1d49e, 0x1d49f,
-	0x1d4a5, 0x1d4a6,
-	0x1d4a9, 0x1d4ac,
-	0x1d4ae, 0x1d4b9,
-	0x1d4bd, 0x1d4c3,
-	0x1d4c5, 0x1d505,
-	0x1d507, 0x1d50a,
-	0x1d50d, 0x1d514,
-	0x1d516, 0x1d51c,
-	0x1d51e, 0x1d539,
-	0x1d53b, 0x1d53e,
-	0x1d540, 0x1d544,
-	0x1d54a, 0x1d550,
-	0x1d552, 0x1d6a5,
-	0x1d6a8, 0x1d6c0,
-	0x1d6c2, 0x1d6da,
-	0x1d6dc, 0x1d6fa,
-	0x1d6fc, 0x1d714,
-	0x1d716, 0x1d734,
-	0x1d736, 0x1d74e,
-	0x1d750, 0x1d76e,
-	0x1d770, 0x1d788,
-	0x1d78a, 0x1d7a8,
-	0x1d7aa, 0x1d7c2,
-	0x1d7c4, 0x1d7cb,
-	0x1e800, 0x1e8c4,
-	0x1ee00, 0x1ee03,
-	0x1ee05, 0x1ee1f,
-	0x1ee21, 0x1ee22,
-	0x1ee29, 0x1ee32,
-	0x1ee34, 0x1ee37,
-	0x1ee4d, 0x1ee4f,
-	0x1ee51, 0x1ee52,
-	0x1ee61, 0x1ee62,
-	0x1ee67, 0x1ee6a,
-	0x1ee6c, 0x1ee72,
-	0x1ee74, 0x1ee77,
-	0x1ee79, 0x1ee7c,
-	0x1ee80, 0x1ee89,
-	0x1ee8b, 0x1ee9b,
-	0x1eea1, 0x1eea3,
-	0x1eea5, 0x1eea9,
-	0x1eeab, 0x1eebb,
-	0x20000, 0x2a6d6,
-	0x2a700, 0x2b734,
-	0x2b740, 0x2b81d,
-	0x2f800, 0x2fa1d,
-};
-
-static char32_t isalphas[] = {
-	0x00aa,
-	0x00b5,
-	0x00ba,
-	0x02ec,
-	0x02ee,
-	0x037f,
-	0x0386,
-	0x038c,
-	0x0559,
-	0x06d5,
-	0x06ff,
-	0x0710,
-	0x07b1,
-	0x07fa,
-	0x081a,
-	0x0824,
-	0x0828,
-	0x093d,
-	0x0950,
-	0x09b2,
-	0x09bd,
-	0x09ce,
-	0x0a5e,
-	0x0abd,
-	0x0ad0,
-	0x0b3d,
-	0x0b71,
-	0x0b83,
-	0x0b9c,
-	0x0bd0,
-	0x0c3d,
-	0x0cbd,
-	0x0cde,
-	0x0d3d,
-	0x0d4e,
-	0x0dbd,
-	0x0e84,
-	0x0e8a,
-	0x0e8d,
-	0x0ea5,
-	0x0ea7,
-	0x0ebd,
-	0x0ec6,
-	0x0f00,
-	0x103f,
-	0x1061,
-	0x108e,
-	0x10c7,
-	0x10cd,
-	0x1258,
-	0x12c0,
-	0x17d7,
-	0x17dc,
-	0x18aa,
-	0x1aa7,
-	0x1f59,
-	0x1f5b,
-	0x1f5d,
-	0x1fbe,
-	0x2071,
-	0x207f,
-	0x2102,
-	0x2107,
-	0x2115,
-	0x2124,
-	0x2126,
-	0x2128,
-	0x214e,
-	0x2d27,
-	0x2d2d,
-	0x2d6f,
-	0x2e2f,
-	0xa8fb,
-	0xa9cf,
-	0xaa7a,
-	0xaab1,
-	0xaac0,
-	0xaac2,
-	0xfb1d,
-	0xfb3e,
-	0x10808,
-	0x1083c,
-	0x10a00,
-	0x11176,
-	0x111da,
-	0x1133d,
-	0x114c7,
-	0x11644,
-	0x118ff,
-	0x16f50,
-	0x1d4a2,
-	0x1d4bb,
-	0x1d546,
-	0x1ee24,
-	0x1ee27,
-	0x1ee39,
-	0x1ee3b,
-	0x1ee42,
-	0x1ee47,
-	0x1ee49,
-	0x1ee4b,
-	0x1ee54,
-	0x1ee57,
-	0x1ee59,
-	0x1ee5b,
-	0x1ee5d,
-	0x1ee5f,
-	0x1ee64,
-	0x1ee7e,
-};
-
-bool isalpha(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, isalphar, nelem (isalphar)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	p = rbsearch(c, isalphas, nelem (isalphas), 1);
-	if (p && c == p[0])
-		return true;
-
-	return false;
-}
-
-static char32_t isupperr[] = {
-	0x0041, 0x005a,
-	0x00c0, 0x00d6,
-	0x00d8, 0x00de,
-	0x0178, 0x0179,
-	0x0181, 0x0182,
-	0x0186, 0x0187,
-	0x0189, 0x018b,
-	0x018e, 0x0191,
-	0x0193, 0x0194,
-	0x0196, 0x0198,
-	0x019c, 0x019d,
-	0x019f, 0x01a0,
-	0x01a6, 0x01a7,
-	0x01ae, 0x01af,
-	0x01b1, 0x01b3,
-	0x01b7, 0x01b8,
-	0x01f6, 0x01f8,
-	0x023a, 0x023b,
-	0x023d, 0x023e,
-	0x0243, 0x0246,
-	0x0388, 0x038a,
-	0x038e, 0x038f,
-	0x0391, 0x03a1,
-	0x03a3, 0x03ab,
-	0x03d2, 0x03d4,
-	0x03f9, 0x03fa,
-	0x03fd, 0x042f,
-	0x04c0, 0x04c1,
-	0x0531, 0x0556,
-	0x10a0, 0x10c5,
-	0x1f08, 0x1f0f,
-	0x1f18, 0x1f1d,
-	0x1f28, 0x1f2f,
-	0x1f38, 0x1f3f,
-	0x1f48, 0x1f4d,
-	0x1f68, 0x1f6f,
-	0x1f88, 0x1f8f,
-	0x1f98, 0x1f9f,
-	0x1fa8, 0x1faf,
-	0x1fb8, 0x1fbc,
-	0x1fc8, 0x1fcc,
-	0x1fd8, 0x1fdb,
-	0x1fe8, 0x1fec,
-	0x1ff8, 0x1ffc,
-	0x210b, 0x210d,
-	0x2110, 0x2112,
-	0x2119, 0x211d,
-	0x212a, 0x212d,
-	0x2130, 0x2133,
-	0x213e, 0x213f,
-	0x2160, 0x216f,
-	0x24b6, 0x24cf,
-	0x2c00, 0x2c2e,
-	0x2c62, 0x2c64,
-	0x2c6d, 0x2c70,
-	0x2c7e, 0x2c80,
-	0xa77d, 0xa77e,
-	0xa7aa, 0xa7ad,
-	0xa7b0, 0xa7b1,
-	0xff21, 0xff3a,
-	0x10400, 0x10427,
-	0x118a0, 0x118bf,
-	0x1d400, 0x1d419,
-	0x1d434, 0x1d44d,
-	0x1d468, 0x1d481,
-	0x1d49e, 0x1d49f,
-	0x1d4a5, 0x1d4a6,
-	0x1d4a9, 0x1d4ac,
-	0x1d4ae, 0x1d4b5,
-	0x1d4d0, 0x1d4e9,
-	0x1d504, 0x1d505,
-	0x1d507, 0x1d50a,
-	0x1d50d, 0x1d514,
-	0x1d516, 0x1d51c,
-	0x1d538, 0x1d539,
-	0x1d53b, 0x1d53e,
-	0x1d540, 0x1d544,
-	0x1d54a, 0x1d550,
-	0x1d56c, 0x1d585,
-	0x1d5a0, 0x1d5b9,
-	0x1d5d4, 0x1d5ed,
-	0x1d608, 0x1d621,
-	0x1d63c, 0x1d655,
-	0x1d670, 0x1d689,
-	0x1d6a8, 0x1d6c0,
-	0x1d6e2, 0x1d6fa,
-	0x1d71c, 0x1d734,
-	0x1d756, 0x1d76e,
-	0x1d790, 0x1d7a8,
-};
-
-static char32_t isuppers[] = {
-	0x0100,
-	0x0102,
-	0x0104,
-	0x0106,
-	0x0108,
-	0x010a,
-	0x010c,
-	0x010e,
-	0x0110,
-	0x0112,
-	0x0114,
-	0x0116,
-	0x0118,
-	0x011a,
-	0x011c,
-	0x011e,
-	0x0120,
-	0x0122,
-	0x0124,
-	0x0126,
-	0x0128,
-	0x012a,
-	0x012c,
-	0x012e,
-	0x0130,
-	0x0132,
-	0x0134,
-	0x0136,
-	0x0139,
-	0x013b,
-	0x013d,
-	0x013f,
-	0x0141,
-	0x0143,
-	0x0145,
-	0x0147,
-	0x014a,
-	0x014c,
-	0x014e,
-	0x0150,
-	0x0152,
-	0x0154,
-	0x0156,
-	0x0158,
-	0x015a,
-	0x015c,
-	0x015e,
-	0x0160,
-	0x0162,
-	0x0164,
-	0x0166,
-	0x0168,
-	0x016a,
-	0x016c,
-	0x016e,
-	0x0170,
-	0x0172,
-	0x0174,
-	0x0176,
-	0x017b,
-	0x017d,
-	0x0184,
-	0x01a2,
-	0x01a4,
-	0x01a9,
-	0x01ac,
-	0x01b5,
-	0x01bc,
-	0x01c4,
-	0x01c7,
-	0x01ca,
-	0x01cd,
-	0x01cf,
-	0x01d1,
-	0x01d3,
-	0x01d5,
-	0x01d7,
-	0x01d9,
-	0x01db,
-	0x01de,
-	0x01e0,
-	0x01e2,
-	0x01e4,
-	0x01e6,
-	0x01e8,
-	0x01ea,
-	0x01ec,
-	0x01ee,
-	0x01f1,
-	0x01f4,
-	0x01fa,
-	0x01fc,
-	0x01fe,
-	0x0200,
-	0x0202,
-	0x0204,
-	0x0206,
-	0x0208,
-	0x020a,
-	0x020c,
-	0x020e,
-	0x0210,
-	0x0212,
-	0x0214,
-	0x0216,
-	0x0218,
-	0x021a,
-	0x021c,
-	0x021e,
-	0x0220,
-	0x0222,
-	0x0224,
-	0x0226,
-	0x0228,
-	0x022a,
-	0x022c,
-	0x022e,
-	0x0230,
-	0x0232,
-	0x0241,
-	0x0248,
-	0x024a,
-	0x024c,
-	0x024e,
-	0x0370,
-	0x0372,
-	0x0376,
-	0x037f,
-	0x0386,
-	0x038c,
-	0x03cf,
-	0x03d8,
-	0x03da,
-	0x03dc,
-	0x03de,
-	0x03e0,
-	0x03e2,
-	0x03e4,
-	0x03e6,
-	0x03e8,
-	0x03ea,
-	0x03ec,
-	0x03ee,
-	0x03f4,
-	0x03f7,
-	0x0460,
-	0x0462,
-	0x0464,
-	0x0466,
-	0x0468,
-	0x046a,
-	0x046c,
-	0x046e,
-	0x0470,
-	0x0472,
-	0x0474,
-	0x0476,
-	0x0478,
-	0x047a,
-	0x047c,
-	0x047e,
-	0x0480,
-	0x048a,
-	0x048c,
-	0x048e,
-	0x0490,
-	0x0492,
-	0x0494,
-	0x0496,
-	0x0498,
-	0x049a,
-	0x049c,
-	0x049e,
-	0x04a0,
-	0x04a2,
-	0x04a4,
-	0x04a6,
-	0x04a8,
-	0x04aa,
-	0x04ac,
-	0x04ae,
-	0x04b0,
-	0x04b2,
-	0x04b4,
-	0x04b6,
-	0x04b8,
-	0x04ba,
-	0x04bc,
-	0x04be,
-	0x04c3,
-	0x04c5,
-	0x04c7,
-	0x04c9,
-	0x04cb,
-	0x04cd,
-	0x04d0,
-	0x04d2,
-	0x04d4,
-	0x04d6,
-	0x04d8,
-	0x04da,
-	0x04dc,
-	0x04de,
-	0x04e0,
-	0x04e2,
-	0x04e4,
-	0x04e6,
-	0x04e8,
-	0x04ea,
-	0x04ec,
-	0x04ee,
-	0x04f0,
-	0x04f2,
-	0x04f4,
-	0x04f6,
-	0x04f8,
-	0x04fa,
-	0x04fc,
-	0x04fe,
-	0x0500,
-	0x0502,
-	0x0504,
-	0x0506,
-	0x0508,
-	0x050a,
-	0x050c,
-	0x050e,
-	0x0510,
-	0x0512,
-	0x0514,
-	0x0516,
-	0x0518,
-	0x051a,
-	0x051c,
-	0x051e,
-	0x0520,
-	0x0522,
-	0x0524,
-	0x0526,
-	0x0528,
-	0x052a,
-	0x052c,
-	0x052e,
-	0x10c7,
-	0x10cd,
-	0x1e00,
-	0x1e02,
-	0x1e04,
-	0x1e06,
-	0x1e08,
-	0x1e0a,
-	0x1e0c,
-	0x1e0e,
-	0x1e10,
-	0x1e12,
-	0x1e14,
-	0x1e16,
-	0x1e18,
-	0x1e1a,
-	0x1e1c,
-	0x1e1e,
-	0x1e20,
-	0x1e22,
-	0x1e24,
-	0x1e26,
-	0x1e28,
-	0x1e2a,
-	0x1e2c,
-	0x1e2e,
-	0x1e30,
-	0x1e32,
-	0x1e34,
-	0x1e36,
-	0x1e38,
-	0x1e3a,
-	0x1e3c,
-	0x1e3e,
-	0x1e40,
-	0x1e42,
-	0x1e44,
-	0x1e46,
-	0x1e48,
-	0x1e4a,
-	0x1e4c,
-	0x1e4e,
-	0x1e50,
-	0x1e52,
-	0x1e54,
-	0x1e56,
-	0x1e58,
-	0x1e5a,
-	0x1e5c,
-	0x1e5e,
-	0x1e60,
-	0x1e62,
-	0x1e64,
-	0x1e66,
-	0x1e68,
-	0x1e6a,
-	0x1e6c,
-	0x1e6e,
-	0x1e70,
-	0x1e72,
-	0x1e74,
-	0x1e76,
-	0x1e78,
-	0x1e7a,
-	0x1e7c,
-	0x1e7e,
-	0x1e80,
-	0x1e82,
-	0x1e84,
-	0x1e86,
-	0x1e88,
-	0x1e8a,
-	0x1e8c,
-	0x1e8e,
-	0x1e90,
-	0x1e92,
-	0x1e94,
-	0x1e9e,
-	0x1ea0,
-	0x1ea2,
-	0x1ea4,
-	0x1ea6,
-	0x1ea8,
-	0x1eaa,
-	0x1eac,
-	0x1eae,
-	0x1eb0,
-	0x1eb2,
-	0x1eb4,
-	0x1eb6,
-	0x1eb8,
-	0x1eba,
-	0x1ebc,
-	0x1ebe,
-	0x1ec0,
-	0x1ec2,
-	0x1ec4,
-	0x1ec6,
-	0x1ec8,
-	0x1eca,
-	0x1ecc,
-	0x1ece,
-	0x1ed0,
-	0x1ed2,
-	0x1ed4,
-	0x1ed6,
-	0x1ed8,
-	0x1eda,
-	0x1edc,
-	0x1ede,
-	0x1ee0,
-	0x1ee2,
-	0x1ee4,
-	0x1ee6,
-	0x1ee8,
-	0x1eea,
-	0x1eec,
-	0x1eee,
-	0x1ef0,
-	0x1ef2,
-	0x1ef4,
-	0x1ef6,
-	0x1ef8,
-	0x1efa,
-	0x1efc,
-	0x1efe,
-	0x1f59,
-	0x1f5b,
-	0x1f5d,
-	0x1f5f,
-	0x2102,
-	0x2107,
-	0x2115,
-	0x2124,
-	0x2126,
-	0x2128,
-	0x2145,
-	0x2183,
-	0x2c60,
-	0x2c67,
-	0x2c69,
-	0x2c6b,
-	0x2c72,
-	0x2c75,
-	0x2c82,
-	0x2c84,
-	0x2c86,
-	0x2c88,
-	0x2c8a,
-	0x2c8c,
-	0x2c8e,
-	0x2c90,
-	0x2c92,
-	0x2c94,
-	0x2c96,
-	0x2c98,
-	0x2c9a,
-	0x2c9c,
-	0x2c9e,
-	0x2ca0,
-	0x2ca2,
-	0x2ca4,
-	0x2ca6,
-	0x2ca8,
-	0x2caa,
-	0x2cac,
-	0x2cae,
-	0x2cb0,
-	0x2cb2,
-	0x2cb4,
-	0x2cb6,
-	0x2cb8,
-	0x2cba,
-	0x2cbc,
-	0x2cbe,
-	0x2cc0,
-	0x2cc2,
-	0x2cc4,
-	0x2cc6,
-	0x2cc8,
-	0x2cca,
-	0x2ccc,
-	0x2cce,
-	0x2cd0,
-	0x2cd2,
-	0x2cd4,
-	0x2cd6,
-	0x2cd8,
-	0x2cda,
-	0x2cdc,
-	0x2cde,
-	0x2ce0,
-	0x2ce2,
-	0x2ceb,
-	0x2ced,
-	0x2cf2,
-	0xa640,
-	0xa642,
-	0xa644,
-	0xa646,
-	0xa648,
-	0xa64a,
-	0xa64c,
-	0xa64e,
-	0xa650,
-	0xa652,
-	0xa654,
-	0xa656,
-	0xa658,
-	0xa65a,
-	0xa65c,
-	0xa65e,
-	0xa660,
-	0xa662,
-	0xa664,
-	0xa666,
-	0xa668,
-	0xa66a,
-	0xa66c,
-	0xa680,
-	0xa682,
-	0xa684,
-	0xa686,
-	0xa688,
-	0xa68a,
-	0xa68c,
-	0xa68e,
-	0xa690,
-	0xa692,
-	0xa694,
-	0xa696,
-	0xa698,
-	0xa69a,
-	0xa722,
-	0xa724,
-	0xa726,
-	0xa728,
-	0xa72a,
-	0xa72c,
-	0xa72e,
-	0xa732,
-	0xa734,
-	0xa736,
-	0xa738,
-	0xa73a,
-	0xa73c,
-	0xa73e,
-	0xa740,
-	0xa742,
-	0xa744,
-	0xa746,
-	0xa748,
-	0xa74a,
-	0xa74c,
-	0xa74e,
-	0xa750,
-	0xa752,
-	0xa754,
-	0xa756,
-	0xa758,
-	0xa75a,
-	0xa75c,
-	0xa75e,
-	0xa760,
-	0xa762,
-	0xa764,
-	0xa766,
-	0xa768,
-	0xa76a,
-	0xa76c,
-	0xa76e,
-	0xa779,
-	0xa77b,
-	0xa780,
-	0xa782,
-	0xa784,
-	0xa786,
-	0xa78b,
-	0xa78d,
-	0xa790,
-	0xa792,
-	0xa796,
-	0xa798,
-	0xa79a,
-	0xa79c,
-	0xa79e,
-	0xa7a0,
-	0xa7a2,
-	0xa7a4,
-	0xa7a6,
-	0xa7a8,
-	0x1d49c,
-	0x1d4a2,
-	0x1d546,
-	0x1d7ca,
-};
-
-bool isupper(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, isupperr, nelem (isupperr)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	p = rbsearch(c, isuppers, nelem (isuppers), 1);
-	if (p && c == p[0])
-		return true;
-
-	return false;
-}
-
-static char32_t islowerr[] = {
-	0x0061, 0x007a,
-	0x00df, 0x00f6,
-	0x00f8, 0x00ff,
-	0x0137, 0x0138,
-	0x0148, 0x0149,
-	0x017e, 0x0180,
-	0x018c, 0x018d,
-	0x0199, 0x019b,
-	0x01aa, 0x01ab,
-	0x01b9, 0x01ba,
-	0x01bd, 0x01bf,
-	0x01dc, 0x01dd,
-	0x01ef, 0x01f0,
-	0x0233, 0x0239,
-	0x023f, 0x0240,
-	0x024f, 0x0293,
-	0x0295, 0x02af,
-	0x037b, 0x037d,
-	0x03ac, 0x03ce,
-	0x03d0, 0x03d1,
-	0x03d5, 0x03d7,
-	0x03ef, 0x03f3,
-	0x03fb, 0x03fc,
-	0x0430, 0x045f,
-	0x04ce, 0x04cf,
-	0x0561, 0x0587,
-	0x1d00, 0x1d2b,
-	0x1d6b, 0x1d77,
-	0x1d79, 0x1d9a,
-	0x1e95, 0x1e9d,
-	0x1eff, 0x1f07,
-	0x1f10, 0x1f15,
-	0x1f20, 0x1f27,
-	0x1f30, 0x1f37,
-	0x1f40, 0x1f45,
-	0x1f50, 0x1f57,
-	0x1f60, 0x1f67,
-	0x1f70, 0x1f7d,
-	0x1f80, 0x1f87,
-	0x1f90, 0x1f97,
-	0x1fa0, 0x1fa7,
-	0x1fb0, 0x1fb4,
-	0x1fb6, 0x1fb7,
-	0x1fc2, 0x1fc4,
-	0x1fc6, 0x1fc7,
-	0x1fd0, 0x1fd3,
-	0x1fd6, 0x1fd7,
-	0x1fe0, 0x1fe7,
-	0x1ff2, 0x1ff4,
-	0x1ff6, 0x1ff7,
-	0x210e, 0x210f,
-	0x213c, 0x213d,
-	0x2146, 0x2149,
-	0x2170, 0x217f,
-	0x24d0, 0x24e9,
-	0x2c30, 0x2c5e,
-	0x2c65, 0x2c66,
-	0x2c73, 0x2c74,
-	0x2c76, 0x2c7b,
-	0x2ce3, 0x2ce4,
-	0x2d00, 0x2d25,
-	0xa72f, 0xa731,
-	0xa771, 0xa778,
-	0xa793, 0xa795,
-	0xab30, 0xab5a,
-	0xab64, 0xab65,
-	0xfb00, 0xfb06,
-	0xfb13, 0xfb17,
-	0xff41, 0xff5a,
-	0x10428, 0x1044f,
-	0x118c0, 0x118df,
-	0x1d41a, 0x1d433,
-	0x1d44e, 0x1d454,
-	0x1d456, 0x1d467,
-	0x1d482, 0x1d49b,
-	0x1d4b6, 0x1d4b9,
-	0x1d4bd, 0x1d4c3,
-	0x1d4c5, 0x1d4cf,
-	0x1d4ea, 0x1d503,
-	0x1d51e, 0x1d537,
-	0x1d552, 0x1d56b,
-	0x1d586, 0x1d59f,
-	0x1d5ba, 0x1d5d3,
-	0x1d5ee, 0x1d607,
-	0x1d622, 0x1d63b,
-	0x1d656, 0x1d66f,
-	0x1d68a, 0x1d6a5,
-	0x1d6c2, 0x1d6da,
-	0x1d6dc, 0x1d6e1,
-	0x1d6fc, 0x1d714,
-	0x1d716, 0x1d71b,
-	0x1d736, 0x1d74e,
-	0x1d750, 0x1d755,
-	0x1d770, 0x1d788,
-	0x1d78a, 0x1d78f,
-	0x1d7aa, 0x1d7c2,
-	0x1d7c4, 0x1d7c9,
-};
-
-static char32_t islowers[] = {
-	0x00b5,
-	0x0101,
-	0x0103,
-	0x0105,
-	0x0107,
-	0x0109,
-	0x010b,
-	0x010d,
-	0x010f,
-	0x0111,
-	0x0113,
-	0x0115,
-	0x0117,
-	0x0119,
-	0x011b,
-	0x011d,
-	0x011f,
-	0x0121,
-	0x0123,
-	0x0125,
-	0x0127,
-	0x0129,
-	0x012b,
-	0x012d,
-	0x012f,
-	0x0131,
-	0x0133,
-	0x0135,
-	0x013a,
-	0x013c,
-	0x013e,
-	0x0140,
-	0x0142,
-	0x0144,
-	0x0146,
-	0x014b,
-	0x014d,
-	0x014f,
-	0x0151,
-	0x0153,
-	0x0155,
-	0x0157,
-	0x0159,
-	0x015b,
-	0x015d,
-	0x015f,
-	0x0161,
-	0x0163,
-	0x0165,
-	0x0167,
-	0x0169,
-	0x016b,
-	0x016d,
-	0x016f,
-	0x0171,
-	0x0173,
-	0x0175,
-	0x0177,
-	0x017a,
-	0x017c,
-	0x0183,
-	0x0185,
-	0x0188,
-	0x0192,
-	0x0195,
-	0x019e,
-	0x01a1,
-	0x01a3,
-	0x01a5,
-	0x01a8,
-	0x01ad,
-	0x01b0,
-	0x01b4,
-	0x01b6,
-	0x01c6,
-	0x01c9,
-	0x01cc,
-	0x01ce,
-	0x01d0,
-	0x01d2,
-	0x01d4,
-	0x01d6,
-	0x01d8,
-	0x01da,
-	0x01df,
-	0x01e1,
-	0x01e3,
-	0x01e5,
-	0x01e7,
-	0x01e9,
-	0x01eb,
-	0x01ed,
-	0x01f3,
-	0x01f5,
-	0x01f9,
-	0x01fb,
-	0x01fd,
-	0x01ff,
-	0x0201,
-	0x0203,
-	0x0205,
-	0x0207,
-	0x0209,
-	0x020b,
-	0x020d,
-	0x020f,
-	0x0211,
-	0x0213,
-	0x0215,
-	0x0217,
-	0x0219,
-	0x021b,
-	0x021d,
-	0x021f,
-	0x0221,
-	0x0223,
-	0x0225,
-	0x0227,
-	0x0229,
-	0x022b,
-	0x022d,
-	0x022f,
-	0x0231,
-	0x023c,
-	0x0242,
-	0x0247,
-	0x0249,
-	0x024b,
-	0x024d,
-	0x0371,
-	0x0373,
-	0x0377,
-	0x0390,
-	0x03d9,
-	0x03db,
-	0x03dd,
-	0x03df,
-	0x03e1,
-	0x03e3,
-	0x03e5,
-	0x03e7,
-	0x03e9,
-	0x03eb,
-	0x03ed,
-	0x03f5,
-	0x03f8,
-	0x0461,
-	0x0463,
-	0x0465,
-	0x0467,
-	0x0469,
-	0x046b,
-	0x046d,
-	0x046f,
-	0x0471,
-	0x0473,
-	0x0475,
-	0x0477,
-	0x0479,
-	0x047b,
-	0x047d,
-	0x047f,
-	0x0481,
-	0x048b,
-	0x048d,
-	0x048f,
-	0x0491,
-	0x0493,
-	0x0495,
-	0x0497,
-	0x0499,
-	0x049b,
-	0x049d,
-	0x049f,
-	0x04a1,
-	0x04a3,
-	0x04a5,
-	0x04a7,
-	0x04a9,
-	0x04ab,
-	0x04ad,
-	0x04af,
-	0x04b1,
-	0x04b3,
-	0x04b5,
-	0x04b7,
-	0x04b9,
-	0x04bb,
-	0x04bd,
-	0x04bf,
-	0x04c2,
-	0x04c4,
-	0x04c6,
-	0x04c8,
-	0x04ca,
-	0x04cc,
-	0x04d1,
-	0x04d3,
-	0x04d5,
-	0x04d7,
-	0x04d9,
-	0x04db,
-	0x04dd,
-	0x04df,
-	0x04e1,
-	0x04e3,
-	0x04e5,
-	0x04e7,
-	0x04e9,
-	0x04eb,
-	0x04ed,
-	0x04ef,
-	0x04f1,
-	0x04f3,
-	0x04f5,
-	0x04f7,
-	0x04f9,
-	0x04fb,
-	0x04fd,
-	0x04ff,
-	0x0501,
-	0x0503,
-	0x0505,
-	0x0507,
-	0x0509,
-	0x050b,
-	0x050d,
-	0x050f,
-	0x0511,
-	0x0513,
-	0x0515,
-	0x0517,
-	0x0519,
-	0x051b,
-	0x051d,
-	0x051f,
-	0x0521,
-	0x0523,
-	0x0525,
-	0x0527,
-	0x0529,
-	0x052b,
-	0x052d,
-	0x052f,
-	0x1e01,
-	0x1e03,
-	0x1e05,
-	0x1e07,
-	0x1e09,
-	0x1e0b,
-	0x1e0d,
-	0x1e0f,
-	0x1e11,
-	0x1e13,
-	0x1e15,
-	0x1e17,
-	0x1e19,
-	0x1e1b,
-	0x1e1d,
-	0x1e1f,
-	0x1e21,
-	0x1e23,
-	0x1e25,
-	0x1e27,
-	0x1e29,
-	0x1e2b,
-	0x1e2d,
-	0x1e2f,
-	0x1e31,
-	0x1e33,
-	0x1e35,
-	0x1e37,
-	0x1e39,
-	0x1e3b,
-	0x1e3d,
-	0x1e3f,
-	0x1e41,
-	0x1e43,
-	0x1e45,
-	0x1e47,
-	0x1e49,
-	0x1e4b,
-	0x1e4d,
-	0x1e4f,
-	0x1e51,
-	0x1e53,
-	0x1e55,
-	0x1e57,
-	0x1e59,
-	0x1e5b,
-	0x1e5d,
-	0x1e5f,
-	0x1e61,
-	0x1e63,
-	0x1e65,
-	0x1e67,
-	0x1e69,
-	0x1e6b,
-	0x1e6d,
-	0x1e6f,
-	0x1e71,
-	0x1e73,
-	0x1e75,
-	0x1e77,
-	0x1e79,
-	0x1e7b,
-	0x1e7d,
-	0x1e7f,
-	0x1e81,
-	0x1e83,
-	0x1e85,
-	0x1e87,
-	0x1e89,
-	0x1e8b,
-	0x1e8d,
-	0x1e8f,
-	0x1e91,
-	0x1e93,
-	0x1e9f,
-	0x1ea1,
-	0x1ea3,
-	0x1ea5,
-	0x1ea7,
-	0x1ea9,
-	0x1eab,
-	0x1ead,
-	0x1eaf,
-	0x1eb1,
-	0x1eb3,
-	0x1eb5,
-	0x1eb7,
-	0x1eb9,
-	0x1ebb,
-	0x1ebd,
-	0x1ebf,
-	0x1ec1,
-	0x1ec3,
-	0x1ec5,
-	0x1ec7,
-	0x1ec9,
-	0x1ecb,
-	0x1ecd,
-	0x1ecf,
-	0x1ed1,
-	0x1ed3,
-	0x1ed5,
-	0x1ed7,
-	0x1ed9,
-	0x1edb,
-	0x1edd,
-	0x1edf,
-	0x1ee1,
-	0x1ee3,
-	0x1ee5,
-	0x1ee7,
-	0x1ee9,
-	0x1eeb,
-	0x1eed,
-	0x1eef,
-	0x1ef1,
-	0x1ef3,
-	0x1ef5,
-	0x1ef7,
-	0x1ef9,
-	0x1efb,
-	0x1efd,
-	0x1fbe,
-	0x210a,
-	0x2113,
-	0x212f,
-	0x2134,
-	0x2139,
-	0x214e,
-	0x2184,
-	0x2c61,
-	0x2c68,
-	0x2c6a,
-	0x2c6c,
-	0x2c71,
-	0x2c81,
-	0x2c83,
-	0x2c85,
-	0x2c87,
-	0x2c89,
-	0x2c8b,
-	0x2c8d,
-	0x2c8f,
-	0x2c91,
-	0x2c93,
-	0x2c95,
-	0x2c97,
-	0x2c99,
-	0x2c9b,
-	0x2c9d,
-	0x2c9f,
-	0x2ca1,
-	0x2ca3,
-	0x2ca5,
-	0x2ca7,
-	0x2ca9,
-	0x2cab,
-	0x2cad,
-	0x2caf,
-	0x2cb1,
-	0x2cb3,
-	0x2cb5,
-	0x2cb7,
-	0x2cb9,
-	0x2cbb,
-	0x2cbd,
-	0x2cbf,
-	0x2cc1,
-	0x2cc3,
-	0x2cc5,
-	0x2cc7,
-	0x2cc9,
-	0x2ccb,
-	0x2ccd,
-	0x2ccf,
-	0x2cd1,
-	0x2cd3,
-	0x2cd5,
-	0x2cd7,
-	0x2cd9,
-	0x2cdb,
-	0x2cdd,
-	0x2cdf,
-	0x2ce1,
-	0x2cec,
-	0x2cee,
-	0x2cf3,
-	0x2d27,
-	0x2d2d,
-	0xa641,
-	0xa643,
-	0xa645,
-	0xa647,
-	0xa649,
-	0xa64b,
-	0xa64d,
-	0xa64f,
-	0xa651,
-	0xa653,
-	0xa655,
-	0xa657,
-	0xa659,
-	0xa65b,
-	0xa65d,
-	0xa65f,
-	0xa661,
-	0xa663,
-	0xa665,
-	0xa667,
-	0xa669,
-	0xa66b,
-	0xa66d,
-	0xa681,
-	0xa683,
-	0xa685,
-	0xa687,
-	0xa689,
-	0xa68b,
-	0xa68d,
-	0xa68f,
-	0xa691,
-	0xa693,
-	0xa695,
-	0xa697,
-	0xa699,
-	0xa69b,
-	0xa723,
-	0xa725,
-	0xa727,
-	0xa729,
-	0xa72b,
-	0xa72d,
-	0xa733,
-	0xa735,
-	0xa737,
-	0xa739,
-	0xa73b,
-	0xa73d,
-	0xa73f,
-	0xa741,
-	0xa743,
-	0xa745,
-	0xa747,
-	0xa749,
-	0xa74b,
-	0xa74d,
-	0xa74f,
-	0xa751,
-	0xa753,
-	0xa755,
-	0xa757,
-	0xa759,
-	0xa75b,
-	0xa75d,
-	0xa75f,
-	0xa761,
-	0xa763,
-	0xa765,
-	0xa767,
-	0xa769,
-	0xa76b,
-	0xa76d,
-	0xa76f,
-	0xa77a,
-	0xa77c,
-	0xa77f,
-	0xa781,
-	0xa783,
-	0xa785,
-	0xa787,
-	0xa78c,
-	0xa78e,
-	0xa791,
-	0xa797,
-	0xa799,
-	0xa79b,
-	0xa79d,
-	0xa79f,
-	0xa7a1,
-	0xa7a3,
-	0xa7a5,
-	0xa7a7,
-	0xa7a9,
-	0xa7fa,
-	0x1d4bb,
-	0x1d7cb,
-};
-
-bool islower(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, islowerr, nelem (islowerr)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	p = rbsearch(c, islowers, nelem (islowers), 1);
-	if (p && c == p[0])
-		return true;
-
-	return false;
-}
-
-static char32_t istitler[] = {
-	0x0041, 0x005a,
-	0x00c0, 0x00d6,
-	0x00d8, 0x00de,
-	0x0178, 0x0179,
-	0x0181, 0x0182,
-	0x0186, 0x0187,
-	0x0189, 0x018b,
-	0x018e, 0x0191,
-	0x0193, 0x0194,
-	0x0196, 0x0198,
-	0x019c, 0x019d,
-	0x019f, 0x01a0,
-	0x01a6, 0x01a7,
-	0x01ae, 0x01af,
-	0x01b1, 0x01b3,
-	0x01b7, 0x01b8,
-	0x01f6, 0x01f8,
-	0x023a, 0x023b,
-	0x023d, 0x023e,
-	0x0243, 0x0246,
-	0x0388, 0x038a,
-	0x038e, 0x038f,
-	0x0391, 0x03a1,
-	0x03a3, 0x03ab,
-	0x03f9, 0x03fa,
-	0x03fd, 0x042f,
-	0x04c0, 0x04c1,
-	0x0531, 0x0556,
-	0x10a0, 0x10c5,
-	0x1f08, 0x1f0f,
-	0x1f18, 0x1f1d,
-	0x1f28, 0x1f2f,
-	0x1f38, 0x1f3f,
-	0x1f48, 0x1f4d,
-	0x1f68, 0x1f6f,
-	0x1f88, 0x1f8f,
-	0x1f98, 0x1f9f,
-	0x1fa8, 0x1faf,
-	0x1fb8, 0x1fbc,
-	0x1fc8, 0x1fcc,
-	0x1fd8, 0x1fdb,
-	0x1fe8, 0x1fec,
-	0x1ff8, 0x1ffc,
-	0x2160, 0x216f,
-	0x24b6, 0x24cf,
-	0x2c00, 0x2c2e,
-	0x2c62, 0x2c64,
-	0x2c6d, 0x2c70,
-	0x2c7e, 0x2c80,
-	0xa77d, 0xa77e,
-	0xa7aa, 0xa7ad,
-	0xa7b0, 0xa7b1,
-	0xff21, 0xff3a,
-	0x10400, 0x10427,
-	0x118a0, 0x118bf,
-};
-
-static char32_t istitles[] = {
-	0x0100,
-	0x0102,
-	0x0104,
-	0x0106,
-	0x0108,
-	0x010a,
-	0x010c,
-	0x010e,
-	0x0110,
-	0x0112,
-	0x0114,
-	0x0116,
-	0x0118,
-	0x011a,
-	0x011c,
-	0x011e,
-	0x0120,
-	0x0122,
-	0x0124,
-	0x0126,
-	0x0128,
-	0x012a,
-	0x012c,
-	0x012e,
-	0x0132,
-	0x0134,
-	0x0136,
-	0x0139,
-	0x013b,
-	0x013d,
-	0x013f,
-	0x0141,
-	0x0143,
-	0x0145,
-	0x0147,
-	0x014a,
-	0x014c,
-	0x014e,
-	0x0150,
-	0x0152,
-	0x0154,
-	0x0156,
-	0x0158,
-	0x015a,
-	0x015c,
-	0x015e,
-	0x0160,
-	0x0162,
-	0x0164,
-	0x0166,
-	0x0168,
-	0x016a,
-	0x016c,
-	0x016e,
-	0x0170,
-	0x0172,
-	0x0174,
-	0x0176,
-	0x017b,
-	0x017d,
-	0x0184,
-	0x01a2,
-	0x01a4,
-	0x01a9,
-	0x01ac,
-	0x01b5,
-	0x01bc,
-	0x01c5,
-	0x01c8,
-	0x01cb,
-	0x01cd,
-	0x01cf,
-	0x01d1,
-	0x01d3,
-	0x01d5,
-	0x01d7,
-	0x01d9,
-	0x01db,
-	0x01de,
-	0x01e0,
-	0x01e2,
-	0x01e4,
-	0x01e6,
-	0x01e8,
-	0x01ea,
-	0x01ec,
-	0x01ee,
-	0x01f2,
-	0x01f4,
-	0x01fa,
-	0x01fc,
-	0x01fe,
-	0x0200,
-	0x0202,
-	0x0204,
-	0x0206,
-	0x0208,
-	0x020a,
-	0x020c,
-	0x020e,
-	0x0210,
-	0x0212,
-	0x0214,
-	0x0216,
-	0x0218,
-	0x021a,
-	0x021c,
-	0x021e,
-	0x0220,
-	0x0222,
-	0x0224,
-	0x0226,
-	0x0228,
-	0x022a,
-	0x022c,
-	0x022e,
-	0x0230,
-	0x0232,
-	0x0241,
-	0x0248,
-	0x024a,
-	0x024c,
-	0x024e,
-	0x0370,
-	0x0372,
-	0x0376,
-	0x037f,
-	0x0386,
-	0x038c,
-	0x03cf,
-	0x03d8,
-	0x03da,
-	0x03dc,
-	0x03de,
-	0x03e0,
-	0x03e2,
-	0x03e4,
-	0x03e6,
-	0x03e8,
-	0x03ea,
-	0x03ec,
-	0x03ee,
-	0x03f7,
-	0x0460,
-	0x0462,
-	0x0464,
-	0x0466,
-	0x0468,
-	0x046a,
-	0x046c,
-	0x046e,
-	0x0470,
-	0x0472,
-	0x0474,
-	0x0476,
-	0x0478,
-	0x047a,
-	0x047c,
-	0x047e,
-	0x0480,
-	0x048a,
-	0x048c,
-	0x048e,
-	0x0490,
-	0x0492,
-	0x0494,
-	0x0496,
-	0x0498,
-	0x049a,
-	0x049c,
-	0x049e,
-	0x04a0,
-	0x04a2,
-	0x04a4,
-	0x04a6,
-	0x04a8,
-	0x04aa,
-	0x04ac,
-	0x04ae,
-	0x04b0,
-	0x04b2,
-	0x04b4,
-	0x04b6,
-	0x04b8,
-	0x04ba,
-	0x04bc,
-	0x04be,
-	0x04c3,
-	0x04c5,
-	0x04c7,
-	0x04c9,
-	0x04cb,
-	0x04cd,
-	0x04d0,
-	0x04d2,
-	0x04d4,
-	0x04d6,
-	0x04d8,
-	0x04da,
-	0x04dc,
-	0x04de,
-	0x04e0,
-	0x04e2,
-	0x04e4,
-	0x04e6,
-	0x04e8,
-	0x04ea,
-	0x04ec,
-	0x04ee,
-	0x04f0,
-	0x04f2,
-	0x04f4,
-	0x04f6,
-	0x04f8,
-	0x04fa,
-	0x04fc,
-	0x04fe,
-	0x0500,
-	0x0502,
-	0x0504,
-	0x0506,
-	0x0508,
-	0x050a,
-	0x050c,
-	0x050e,
-	0x0510,
-	0x0512,
-	0x0514,
-	0x0516,
-	0x0518,
-	0x051a,
-	0x051c,
-	0x051e,
-	0x0520,
-	0x0522,
-	0x0524,
-	0x0526,
-	0x0528,
-	0x052a,
-	0x052c,
-	0x052e,
-	0x10c7,
-	0x10cd,
-	0x1e00,
-	0x1e02,
-	0x1e04,
-	0x1e06,
-	0x1e08,
-	0x1e0a,
-	0x1e0c,
-	0x1e0e,
-	0x1e10,
-	0x1e12,
-	0x1e14,
-	0x1e16,
-	0x1e18,
-	0x1e1a,
-	0x1e1c,
-	0x1e1e,
-	0x1e20,
-	0x1e22,
-	0x1e24,
-	0x1e26,
-	0x1e28,
-	0x1e2a,
-	0x1e2c,
-	0x1e2e,
-	0x1e30,
-	0x1e32,
-	0x1e34,
-	0x1e36,
-	0x1e38,
-	0x1e3a,
-	0x1e3c,
-	0x1e3e,
-	0x1e40,
-	0x1e42,
-	0x1e44,
-	0x1e46,
-	0x1e48,
-	0x1e4a,
-	0x1e4c,
-	0x1e4e,
-	0x1e50,
-	0x1e52,
-	0x1e54,
-	0x1e56,
-	0x1e58,
-	0x1e5a,
-	0x1e5c,
-	0x1e5e,
-	0x1e60,
-	0x1e62,
-	0x1e64,
-	0x1e66,
-	0x1e68,
-	0x1e6a,
-	0x1e6c,
-	0x1e6e,
-	0x1e70,
-	0x1e72,
-	0x1e74,
-	0x1e76,
-	0x1e78,
-	0x1e7a,
-	0x1e7c,
-	0x1e7e,
-	0x1e80,
-	0x1e82,
-	0x1e84,
-	0x1e86,
-	0x1e88,
-	0x1e8a,
-	0x1e8c,
-	0x1e8e,
-	0x1e90,
-	0x1e92,
-	0x1e94,
-	0x1ea0,
-	0x1ea2,
-	0x1ea4,
-	0x1ea6,
-	0x1ea8,
-	0x1eaa,
-	0x1eac,
-	0x1eae,
-	0x1eb0,
-	0x1eb2,
-	0x1eb4,
-	0x1eb6,
-	0x1eb8,
-	0x1eba,
-	0x1ebc,
-	0x1ebe,
-	0x1ec0,
-	0x1ec2,
-	0x1ec4,
-	0x1ec6,
-	0x1ec8,
-	0x1eca,
-	0x1ecc,
-	0x1ece,
-	0x1ed0,
-	0x1ed2,
-	0x1ed4,
-	0x1ed6,
-	0x1ed8,
-	0x1eda,
-	0x1edc,
-	0x1ede,
-	0x1ee0,
-	0x1ee2,
-	0x1ee4,
-	0x1ee6,
-	0x1ee8,
-	0x1eea,
-	0x1eec,
-	0x1eee,
-	0x1ef0,
-	0x1ef2,
-	0x1ef4,
-	0x1ef6,
-	0x1ef8,
-	0x1efa,
-	0x1efc,
-	0x1efe,
-	0x1f59,
-	0x1f5b,
-	0x1f5d,
-	0x1f5f,
-	0x2132,
-	0x2183,
-	0x2c60,
-	0x2c67,
-	0x2c69,
-	0x2c6b,
-	0x2c72,
-	0x2c75,
-	0x2c82,
-	0x2c84,
-	0x2c86,
-	0x2c88,
-	0x2c8a,
-	0x2c8c,
-	0x2c8e,
-	0x2c90,
-	0x2c92,
-	0x2c94,
-	0x2c96,
-	0x2c98,
-	0x2c9a,
-	0x2c9c,
-	0x2c9e,
-	0x2ca0,
-	0x2ca2,
-	0x2ca4,
-	0x2ca6,
-	0x2ca8,
-	0x2caa,
-	0x2cac,
-	0x2cae,
-	0x2cb0,
-	0x2cb2,
-	0x2cb4,
-	0x2cb6,
-	0x2cb8,
-	0x2cba,
-	0x2cbc,
-	0x2cbe,
-	0x2cc0,
-	0x2cc2,
-	0x2cc4,
-	0x2cc6,
-	0x2cc8,
-	0x2cca,
-	0x2ccc,
-	0x2cce,
-	0x2cd0,
-	0x2cd2,
-	0x2cd4,
-	0x2cd6,
-	0x2cd8,
-	0x2cda,
-	0x2cdc,
-	0x2cde,
-	0x2ce0,
-	0x2ce2,
-	0x2ceb,
-	0x2ced,
-	0x2cf2,
-	0xa640,
-	0xa642,
-	0xa644,
-	0xa646,
-	0xa648,
-	0xa64a,
-	0xa64c,
-	0xa64e,
-	0xa650,
-	0xa652,
-	0xa654,
-	0xa656,
-	0xa658,
-	0xa65a,
-	0xa65c,
-	0xa65e,
-	0xa660,
-	0xa662,
-	0xa664,
-	0xa666,
-	0xa668,
-	0xa66a,
-	0xa66c,
-	0xa680,
-	0xa682,
-	0xa684,
-	0xa686,
-	0xa688,
-	0xa68a,
-	0xa68c,
-	0xa68e,
-	0xa690,
-	0xa692,
-	0xa694,
-	0xa696,
-	0xa698,
-	0xa69a,
-	0xa722,
-	0xa724,
-	0xa726,
-	0xa728,
-	0xa72a,
-	0xa72c,
-	0xa72e,
-	0xa732,
-	0xa734,
-	0xa736,
-	0xa738,
-	0xa73a,
-	0xa73c,
-	0xa73e,
-	0xa740,
-	0xa742,
-	0xa744,
-	0xa746,
-	0xa748,
-	0xa74a,
-	0xa74c,
-	0xa74e,
-	0xa750,
-	0xa752,
-	0xa754,
-	0xa756,
-	0xa758,
-	0xa75a,
-	0xa75c,
-	0xa75e,
-	0xa760,
-	0xa762,
-	0xa764,
-	0xa766,
-	0xa768,
-	0xa76a,
-	0xa76c,
-	0xa76e,
-	0xa779,
-	0xa77b,
-	0xa780,
-	0xa782,
-	0xa784,
-	0xa786,
-	0xa78b,
-	0xa78d,
-	0xa790,
-	0xa792,
-	0xa796,
-	0xa798,
-	0xa79a,
-	0xa79c,
-	0xa79e,
-	0xa7a0,
-	0xa7a2,
-	0xa7a4,
-	0xa7a6,
-	0xa7a8,
-};
-
-bool istitle(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, istitler, nelem (istitler)/2, 2);
-	if (p && c >= p[0] && c <= p[1])
-		return true;
-
-	p = rbsearch(c, istitles, nelem (istitles), 1);
-	if (p && c == p[0])
-		return true;
-
-	return false;
-}
-
-char32_t toupperr[] = {
-	0x0061, 0x007a, 1048544,
-	0x00e0, 0x00f6, 1048544,
-	0x00f8, 0x00fe, 1048544,
-	0x023f, 0x0240, 1059391,
-	0x0256, 0x0257, 1048371,
-	0x028a, 0x028b, 1048359,
-	0x037b, 0x037d, 1048706,
-	0x03ad, 0x03af, 1048539,
-	0x03b1, 0x03c1, 1048544,
-	0x03c3, 0x03cb, 1048544,
-	0x03cd, 0x03ce, 1048513,
-	0x0430, 0x044f, 1048544,
-	0x0450, 0x045f, 1048496,
-	0x0561, 0x0586, 1048528,
-	0x1f00, 0x1f07, 1048584,
-	0x1f10, 0x1f15, 1048584,
-	0x1f20, 0x1f27, 1048584,
-	0x1f30, 0x1f37, 1048584,
-	0x1f40, 0x1f45, 1048584,
-	0x1f60, 0x1f67, 1048584,
-	0x1f70, 0x1f71, 1048650,
-	0x1f72, 0x1f75, 1048662,
-	0x1f76, 0x1f77, 1048676,
-	0x1f78, 0x1f79, 1048704,
-	0x1f7a, 0x1f7b, 1048688,
-	0x1f7c, 0x1f7d, 1048702,
-	0x1f80, 0x1f87, 1048584,
-	0x1f90, 0x1f97, 1048584,
-	0x1fa0, 0x1fa7, 1048584,
-	0x1fb0, 0x1fb1, 1048584,
-	0x1fd0, 0x1fd1, 1048584,
-	0x1fe0, 0x1fe1, 1048584,
-	0x2170, 0x217f, 1048560,
-	0x24d0, 0x24e9, 1048550,
-	0x2c30, 0x2c5e, 1048528,
-	0x2d00, 0x2d25, 1041312,
-	0xff41, 0xff5a, 1048544,
-	0x10428, 0x1044f, 1048536,
-	0x118c0, 0x118df, 1048544,
-};
-
-static char32_t touppers[] = {
-	0x00b5, 1049319,
-	0x00ff, 1048697,
-	0x0101, 1048575,
-	0x0103, 1048575,
-	0x0105, 1048575,
-	0x0107, 1048575,
-	0x0109, 1048575,
-	0x010b, 1048575,
-	0x010d, 1048575,
-	0x010f, 1048575,
-	0x0111, 1048575,
-	0x0113, 1048575,
-	0x0115, 1048575,
-	0x0117, 1048575,
-	0x0119, 1048575,
-	0x011b, 1048575,
-	0x011d, 1048575,
-	0x011f, 1048575,
-	0x0121, 1048575,
-	0x0123, 1048575,
-	0x0125, 1048575,
-	0x0127, 1048575,
-	0x0129, 1048575,
-	0x012b, 1048575,
-	0x012d, 1048575,
-	0x012f, 1048575,
-	0x0131, 1048344,
-	0x0133, 1048575,
-	0x0135, 1048575,
-	0x0137, 1048575,
-	0x013a, 1048575,
-	0x013c, 1048575,
-	0x013e, 1048575,
-	0x0140, 1048575,
-	0x0142, 1048575,
-	0x0144, 1048575,
-	0x0146, 1048575,
-	0x0148, 1048575,
-	0x014b, 1048575,
-	0x014d, 1048575,
-	0x014f, 1048575,
-	0x0151, 1048575,
-	0x0153, 1048575,
-	0x0155, 1048575,
-	0x0157, 1048575,
-	0x0159, 1048575,
-	0x015b, 1048575,
-	0x015d, 1048575,
-	0x015f, 1048575,
-	0x0161, 1048575,
-	0x0163, 1048575,
-	0x0165, 1048575,
-	0x0167, 1048575,
-	0x0169, 1048575,
-	0x016b, 1048575,
-	0x016d, 1048575,
-	0x016f, 1048575,
-	0x0171, 1048575,
-	0x0173, 1048575,
-	0x0175, 1048575,
-	0x0177, 1048575,
-	0x017a, 1048575,
-	0x017c, 1048575,
-	0x017e, 1048575,
-	0x017f, 1048276,
-	0x0180, 1048771,
-	0x0183, 1048575,
-	0x0185, 1048575,
-	0x0188, 1048575,
-	0x018c, 1048575,
-	0x0192, 1048575,
-	0x0195, 1048673,
-	0x0199, 1048575,
-	0x019a, 1048739,
-	0x019e, 1048706,
-	0x01a1, 1048575,
-	0x01a3, 1048575,
-	0x01a5, 1048575,
-	0x01a8, 1048575,
-	0x01ad, 1048575,
-	0x01b0, 1048575,
-	0x01b4, 1048575,
-	0x01b6, 1048575,
-	0x01b9, 1048575,
-	0x01bd, 1048575,
-	0x01bf, 1048632,
-	0x01c5, 1048575,
-	0x01c6, 1048574,
-	0x01c8, 1048575,
-	0x01c9, 1048574,
-	0x01cb, 1048575,
-	0x01cc, 1048574,
-	0x01ce, 1048575,
-	0x01d0, 1048575,
-	0x01d2, 1048575,
-	0x01d4, 1048575,
-	0x01d6, 1048575,
-	0x01d8, 1048575,
-	0x01da, 1048575,
-	0x01dc, 1048575,
-	0x01dd, 1048497,
-	0x01df, 1048575,
-	0x01e1, 1048575,
-	0x01e3, 1048575,
-	0x01e5, 1048575,
-	0x01e7, 1048575,
-	0x01e9, 1048575,
-	0x01eb, 1048575,
-	0x01ed, 1048575,
-	0x01ef, 1048575,
-	0x01f2, 1048575,
-	0x01f3, 1048574,
-	0x01f5, 1048575,
-	0x01f9, 1048575,
-	0x01fb, 1048575,
-	0x01fd, 1048575,
-	0x01ff, 1048575,
-	0x0201, 1048575,
-	0x0203, 1048575,
-	0x0205, 1048575,
-	0x0207, 1048575,
-	0x0209, 1048575,
-	0x020b, 1048575,
-	0x020d, 1048575,
-	0x020f, 1048575,
-	0x0211, 1048575,
-	0x0213, 1048575,
-	0x0215, 1048575,
-	0x0217, 1048575,
-	0x0219, 1048575,
-	0x021b, 1048575,
-	0x021d, 1048575,
-	0x021f, 1048575,
-	0x0223, 1048575,
-	0x0225, 1048575,
-	0x0227, 1048575,
-	0x0229, 1048575,
-	0x022b, 1048575,
-	0x022d, 1048575,
-	0x022f, 1048575,
-	0x0231, 1048575,
-	0x0233, 1048575,
-	0x023c, 1048575,
-	0x0242, 1048575,
-	0x0247, 1048575,
-	0x0249, 1048575,
-	0x024b, 1048575,
-	0x024d, 1048575,
-	0x024f, 1048575,
-	0x0250, 1059359,
-	0x0251, 1059356,
-	0x0252, 1059358,
-	0x0253, 1048366,
-	0x0254, 1048370,
-	0x0259, 1048374,
-	0x025b, 1048373,
-	0x025c, 1090895,
-	0x0260, 1048371,
-	0x0261, 1090891,
-	0x0263, 1048369,
-	0x0265, 1090856,
-	0x0266, 1090884,
-	0x0268, 1048367,
-	0x0269, 1048365,
-	0x026b, 1059319,
-	0x026c, 1090881,
-	0x026f, 1048365,
-	0x0271, 1059325,
-	0x0272, 1048363,
-	0x0275, 1048362,
-	0x027d, 1059303,
-	0x0280, 1048358,
-	0x0283, 1048358,
-	0x0287, 1090858,
-	0x0288, 1048358,
-	0x0289, 1048507,
-	0x028c, 1048505,
-	0x0292, 1048357,
-	0x029e, 1090834,
-	0x0345, 1048660,
-	0x0371, 1048575,
-	0x0373, 1048575,
-	0x0377, 1048575,
-	0x03ac, 1048538,
-	0x03c2, 1048545,
-	0x03cc, 1048512,
-	0x03d0, 1048514,
-	0x03d1, 1048519,
-	0x03d5, 1048529,
-	0x03d6, 1048522,
-	0x03d7, 1048568,
-	0x03d9, 1048575,
-	0x03db, 1048575,
-	0x03dd, 1048575,
-	0x03df, 1048575,
-	0x03e1, 1048575,
-	0x03e3, 1048575,
-	0x03e5, 1048575,
-	0x03e7, 1048575,
-	0x03e9, 1048575,
-	0x03eb, 1048575,
-	0x03ed, 1048575,
-	0x03ef, 1048575,
-	0x03f0, 1048490,
-	0x03f1, 1048496,
-	0x03f2, 1048583,
-	0x03f3, 1048460,
-	0x03f5, 1048480,
-	0x03f8, 1048575,
-	0x03fb, 1048575,
-	0x0461, 1048575,
-	0x0463, 1048575,
-	0x0465, 1048575,
-	0x0467, 1048575,
-	0x0469, 1048575,
-	0x046b, 1048575,
-	0x046d, 1048575,
-	0x046f, 1048575,
-	0x0471, 1048575,
-	0x0473, 1048575,
-	0x0475, 1048575,
-	0x0477, 1048575,
-	0x0479, 1048575,
-	0x047b, 1048575,
-	0x047d, 1048575,
-	0x047f, 1048575,
-	0x0481, 1048575,
-	0x048b, 1048575,
-	0x048d, 1048575,
-	0x048f, 1048575,
-	0x0491, 1048575,
-	0x0493, 1048575,
-	0x0495, 1048575,
-	0x0497, 1048575,
-	0x0499, 1048575,
-	0x049b, 1048575,
-	0x049d, 1048575,
-	0x049f, 1048575,
-	0x04a1, 1048575,
-	0x04a3, 1048575,
-	0x04a5, 1048575,
-	0x04a7, 1048575,
-	0x04a9, 1048575,
-	0x04ab, 1048575,
-	0x04ad, 1048575,
-	0x04af, 1048575,
-	0x04b1, 1048575,
-	0x04b3, 1048575,
-	0x04b5, 1048575,
-	0x04b7, 1048575,
-	0x04b9, 1048575,
-	0x04bb, 1048575,
-	0x04bd, 1048575,
-	0x04bf, 1048575,
-	0x04c2, 1048575,
-	0x04c4, 1048575,
-	0x04c6, 1048575,
-	0x04c8, 1048575,
-	0x04ca, 1048575,
-	0x04cc, 1048575,
-	0x04ce, 1048575,
-	0x04cf, 1048561,
-	0x04d1, 1048575,
-	0x04d3, 1048575,
-	0x04d5, 1048575,
-	0x04d7, 1048575,
-	0x04d9, 1048575,
-	0x04db, 1048575,
-	0x04dd, 1048575,
-	0x04df, 1048575,
-	0x04e1, 1048575,
-	0x04e3, 1048575,
-	0x04e5, 1048575,
-	0x04e7, 1048575,
-	0x04e9, 1048575,
-	0x04eb, 1048575,
-	0x04ed, 1048575,
-	0x04ef, 1048575,
-	0x04f1, 1048575,
-	0x04f3, 1048575,
-	0x04f5, 1048575,
-	0x04f7, 1048575,
-	0x04f9, 1048575,
-	0x04fb, 1048575,
-	0x04fd, 1048575,
-	0x04ff, 1048575,
-	0x0501, 1048575,
-	0x0503, 1048575,
-	0x0505, 1048575,
-	0x0507, 1048575,
-	0x0509, 1048575,
-	0x050b, 1048575,
-	0x050d, 1048575,
-	0x050f, 1048575,
-	0x0511, 1048575,
-	0x0513, 1048575,
-	0x0515, 1048575,
-	0x0517, 1048575,
-	0x0519, 1048575,
-	0x051b, 1048575,
-	0x051d, 1048575,
-	0x051f, 1048575,
-	0x0521, 1048575,
-	0x0523, 1048575,
-	0x0525, 1048575,
-	0x0527, 1048575,
-	0x0529, 1048575,
-	0x052b, 1048575,
-	0x052d, 1048575,
-	0x052f, 1048575,
-	0x1d79, 1083908,
-	0x1d7d, 1052390,
-	0x1e01, 1048575,
-	0x1e03, 1048575,
-	0x1e05, 1048575,
-	0x1e07, 1048575,
-	0x1e09, 1048575,
-	0x1e0b, 1048575,
-	0x1e0d, 1048575,
-	0x1e0f, 1048575,
-	0x1e11, 1048575,
-	0x1e13, 1048575,
-	0x1e15, 1048575,
-	0x1e17, 1048575,
-	0x1e19, 1048575,
-	0x1e1b, 1048575,
-	0x1e1d, 1048575,
-	0x1e1f, 1048575,
-	0x1e21, 1048575,
-	0x1e23, 1048575,
-	0x1e25, 1048575,
-	0x1e27, 1048575,
-	0x1e29, 1048575,
-	0x1e2b, 1048575,
-	0x1e2d, 1048575,
-	0x1e2f, 1048575,
-	0x1e31, 1048575,
-	0x1e33, 1048575,
-	0x1e35, 1048575,
-	0x1e37, 1048575,
-	0x1e39, 1048575,
-	0x1e3b, 1048575,
-	0x1e3d, 1048575,
-	0x1e3f, 1048575,
-	0x1e41, 1048575,
-	0x1e43, 1048575,
-	0x1e45, 1048575,
-	0x1e47, 1048575,
-	0x1e49, 1048575,
-	0x1e4b, 1048575,
-	0x1e4d, 1048575,
-	0x1e4f, 1048575,
-	0x1e51, 1048575,
-	0x1e53, 1048575,
-	0x1e55, 1048575,
-	0x1e57, 1048575,
-	0x1e59, 1048575,
-	0x1e5b, 1048575,
-	0x1e5d, 1048575,
-	0x1e5f, 1048575,
-	0x1e61, 1048575,
-	0x1e63, 1048575,
-	0x1e65, 1048575,
-	0x1e67, 1048575,
-	0x1e69, 1048575,
-	0x1e6b, 1048575,
-	0x1e6d, 1048575,
-	0x1e6f, 1048575,
-	0x1e71, 1048575,
-	0x1e73, 1048575,
-	0x1e75, 1048575,
-	0x1e77, 1048575,
-	0x1e79, 1048575,
-	0x1e7b, 1048575,
-	0x1e7d, 1048575,
-	0x1e7f, 1048575,
-	0x1e81, 1048575,
-	0x1e83, 1048575,
-	0x1e85, 1048575,
-	0x1e87, 1048575,
-	0x1e89, 1048575,
-	0x1e8b, 1048575,
-	0x1e8d, 1048575,
-	0x1e8f, 1048575,
-	0x1e91, 1048575,
-	0x1e93, 1048575,
-	0x1e95, 1048575,
-	0x1e9b, 1048517,
-	0x1ea1, 1048575,
-	0x1ea3, 1048575,
-	0x1ea5, 1048575,
-	0x1ea7, 1048575,
-	0x1ea9, 1048575,
-	0x1eab, 1048575,
-	0x1ead, 1048575,
-	0x1eaf, 1048575,
-	0x1eb1, 1048575,
-	0x1eb3, 1048575,
-	0x1eb5, 1048575,
-	0x1eb7, 1048575,
-	0x1eb9, 1048575,
-	0x1ebb, 1048575,
-	0x1ebd, 1048575,
-	0x1ebf, 1048575,
-	0x1ec1, 1048575,
-	0x1ec3, 1048575,
-	0x1ec5, 1048575,
-	0x1ec7, 1048575,
-	0x1ec9, 1048575,
-	0x1ecb, 1048575,
-	0x1ecd, 1048575,
-	0x1ecf, 1048575,
-	0x1ed1, 1048575,
-	0x1ed3, 1048575,
-	0x1ed5, 1048575,
-	0x1ed7, 1048575,
-	0x1ed9, 1048575,
-	0x1edb, 1048575,
-	0x1edd, 1048575,
-	0x1edf, 1048575,
-	0x1ee1, 1048575,
-	0x1ee3, 1048575,
-	0x1ee5, 1048575,
-	0x1ee7, 1048575,
-	0x1ee9, 1048575,
-	0x1eeb, 1048575,
-	0x1eed, 1048575,
-	0x1eef, 1048575,
-	0x1ef1, 1048575,
-	0x1ef3, 1048575,
-	0x1ef5, 1048575,
-	0x1ef7, 1048575,
-	0x1ef9, 1048575,
-	0x1efb, 1048575,
-	0x1efd, 1048575,
-	0x1eff, 1048575,
-	0x1f51, 1048584,
-	0x1f53, 1048584,
-	0x1f55, 1048584,
-	0x1f57, 1048584,
-	0x1fb3, 1048585,
-	0x1fbe, 1041371,
-	0x1fc3, 1048585,
-	0x1fe5, 1048583,
-	0x1ff3, 1048585,
-	0x214e, 1048548,
-	0x2184, 1048575,
-	0x2c61, 1048575,
-	0x2c65, 1037781,
-	0x2c66, 1037784,
-	0x2c68, 1048575,
-	0x2c6a, 1048575,
-	0x2c6c, 1048575,
-	0x2c73, 1048575,
-	0x2c76, 1048575,
-	0x2c81, 1048575,
-	0x2c83, 1048575,
-	0x2c85, 1048575,
-	0x2c87, 1048575,
-	0x2c89, 1048575,
-	0x2c8b, 1048575,
-	0x2c8d, 1048575,
-	0x2c8f, 1048575,
-	0x2c91, 1048575,
-	0x2c93, 1048575,
-	0x2c95, 1048575,
-	0x2c97, 1048575,
-	0x2c99, 1048575,
-	0x2c9b, 1048575,
-	0x2c9d, 1048575,
-	0x2c9f, 1048575,
-	0x2ca1, 1048575,
-	0x2ca3, 1048575,
-	0x2ca5, 1048575,
-	0x2ca7, 1048575,
-	0x2ca9, 1048575,
-	0x2cab, 1048575,
-	0x2cad, 1048575,
-	0x2caf, 1048575,
-	0x2cb1, 1048575,
-	0x2cb3, 1048575,
-	0x2cb5, 1048575,
-	0x2cb7, 1048575,
-	0x2cb9, 1048575,
-	0x2cbb, 1048575,
-	0x2cbd, 1048575,
-	0x2cbf, 1048575,
-	0x2cc1, 1048575,
-	0x2cc3, 1048575,
-	0x2cc5, 1048575,
-	0x2cc7, 1048575,
-	0x2cc9, 1048575,
-	0x2ccb, 1048575,
-	0x2ccd, 1048575,
-	0x2ccf, 1048575,
-	0x2cd1, 1048575,
-	0x2cd3, 1048575,
-	0x2cd5, 1048575,
-	0x2cd7, 1048575,
-	0x2cd9, 1048575,
-	0x2cdb, 1048575,
-	0x2cdd, 1048575,
-	0x2cdf, 1048575,
-	0x2ce1, 1048575,
-	0x2ce3, 1048575,
-	0x2cec, 1048575,
-	0x2cee, 1048575,
-	0x2cf3, 1048575,
-	0x2d27, 1041312,
-	0x2d2d, 1041312,
-	0xa641, 1048575,
-	0xa643, 1048575,
-	0xa645, 1048575,
-	0xa647, 1048575,
-	0xa649, 1048575,
-	0xa64b, 1048575,
-	0xa64d, 1048575,
-	0xa64f, 1048575,
-	0xa651, 1048575,
-	0xa653, 1048575,
-	0xa655, 1048575,
-	0xa657, 1048575,
-	0xa659, 1048575,
-	0xa65b, 1048575,
-	0xa65d, 1048575,
-	0xa65f, 1048575,
-	0xa661, 1048575,
-	0xa663, 1048575,
-	0xa665, 1048575,
-	0xa667, 1048575,
-	0xa669, 1048575,
-	0xa66b, 1048575,
-	0xa66d, 1048575,
-	0xa681, 1048575,
-	0xa683, 1048575,
-	0xa685, 1048575,
-	0xa687, 1048575,
-	0xa689, 1048575,
-	0xa68b, 1048575,
-	0xa68d, 1048575,
-	0xa68f, 1048575,
-	0xa691, 1048575,
-	0xa693, 1048575,
-	0xa695, 1048575,
-	0xa697, 1048575,
-	0xa699, 1048575,
-	0xa69b, 1048575,
-	0xa723, 1048575,
-	0xa725, 1048575,
-	0xa727, 1048575,
-	0xa729, 1048575,
-	0xa72b, 1048575,
-	0xa72d, 1048575,
-	0xa72f, 1048575,
-	0xa733, 1048575,
-	0xa735, 1048575,
-	0xa737, 1048575,
-	0xa739, 1048575,
-	0xa73b, 1048575,
-	0xa73d, 1048575,
-	0xa73f, 1048575,
-	0xa741, 1048575,
-	0xa743, 1048575,
-	0xa745, 1048575,
-	0xa747, 1048575,
-	0xa749, 1048575,
-	0xa74b, 1048575,
-	0xa74d, 1048575,
-	0xa74f, 1048575,
-	0xa751, 1048575,
-	0xa753, 1048575,
-	0xa755, 1048575,
-	0xa757, 1048575,
-	0xa759, 1048575,
-	0xa75b, 1048575,
-	0xa75d, 1048575,
-	0xa75f, 1048575,
-	0xa761, 1048575,
-	0xa763, 1048575,
-	0xa765, 1048575,
-	0xa767, 1048575,
-	0xa769, 1048575,
-	0xa76b, 1048575,
-	0xa76d, 1048575,
-	0xa76f, 1048575,
-	0xa77a, 1048575,
-	0xa77c, 1048575,
-	0xa77f, 1048575,
-	0xa781, 1048575,
-	0xa783, 1048575,
-	0xa785, 1048575,
-	0xa787, 1048575,
-	0xa78c, 1048575,
-	0xa791, 1048575,
-	0xa793, 1048575,
-	0xa797, 1048575,
-	0xa799, 1048575,
-	0xa79b, 1048575,
-	0xa79d, 1048575,
-	0xa79f, 1048575,
-	0xa7a1, 1048575,
-	0xa7a3, 1048575,
-	0xa7a5, 1048575,
-	0xa7a7, 1048575,
-	0xa7a9, 1048575,
-};
-
-char32_t toupper(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, toupperr, nelem (toupperr)/3, 3);
-	if (p && c >= p[0] && c <= p[1])
-		return c + p[2] - 1048576;
-
-	p = rbsearch(c, touppers, nelem (touppers)/2, 2);
-	if (p && c == p[0])
-		return c + p[1] - 1048576;
-
-	return c;
-}
-
-char32_t tolowerr[] = {
-	0x0041, 0x005a, 1048608,
-	0x00c0, 0x00d6, 1048608,
-	0x00d8, 0x00de, 1048608,
-	0x0189, 0x018a, 1048781,
-	0x01b1, 0x01b2, 1048793,
-	0x0388, 0x038a, 1048613,
-	0x038e, 0x038f, 1048639,
-	0x0391, 0x03a1, 1048608,
-	0x03a3, 0x03ab, 1048608,
-	0x03fd, 0x03ff, 1048446,
-	0x0400, 0x040f, 1048656,
-	0x0410, 0x042f, 1048608,
-	0x0531, 0x0556, 1048624,
-	0x10a0, 0x10c5, 1055840,
-	0x1f08, 0x1f0f, 1048568,
-	0x1f18, 0x1f1d, 1048568,
-	0x1f28, 0x1f2f, 1048568,
-	0x1f38, 0x1f3f, 1048568,
-	0x1f48, 0x1f4d, 1048568,
-	0x1f68, 0x1f6f, 1048568,
-	0x1f88, 0x1f8f, 1048568,
-	0x1f98, 0x1f9f, 1048568,
-	0x1fa8, 0x1faf, 1048568,
-	0x1fb8, 0x1fb9, 1048568,
-	0x1fba, 0x1fbb, 1048502,
-	0x1fc8, 0x1fcb, 1048490,
-	0x1fd8, 0x1fd9, 1048568,
-	0x1fda, 0x1fdb, 1048476,
-	0x1fe8, 0x1fe9, 1048568,
-	0x1fea, 0x1feb, 1048464,
-	0x1ff8, 0x1ff9, 1048448,
-	0x1ffa, 0x1ffb, 1048450,
-	0x2160, 0x216f, 1048592,
-	0x24b6, 0x24cf, 1048602,
-	0x2c00, 0x2c2e, 1048624,
-	0x2c7e, 0x2c7f, 1037761,
-	0xff21, 0xff3a, 1048608,
-	0x10400, 0x10427, 1048616,
-	0x118a0, 0x118bf, 1048608,
-};
-
-static char32_t tolowers[] = {
-	0x0100, 1048577,
-	0x0102, 1048577,
-	0x0104, 1048577,
-	0x0106, 1048577,
-	0x0108, 1048577,
-	0x010a, 1048577,
-	0x010c, 1048577,
-	0x010e, 1048577,
-	0x0110, 1048577,
-	0x0112, 1048577,
-	0x0114, 1048577,
-	0x0116, 1048577,
-	0x0118, 1048577,
-	0x011a, 1048577,
-	0x011c, 1048577,
-	0x011e, 1048577,
-	0x0120, 1048577,
-	0x0122, 1048577,
-	0x0124, 1048577,
-	0x0126, 1048577,
-	0x0128, 1048577,
-	0x012a, 1048577,
-	0x012c, 1048577,
-	0x012e, 1048577,
-	0x0130, 1048377,
-	0x0132, 1048577,
-	0x0134, 1048577,
-	0x0136, 1048577,
-	0x0139, 1048577,
-	0x013b, 1048577,
-	0x013d, 1048577,
-	0x013f, 1048577,
-	0x0141, 1048577,
-	0x0143, 1048577,
-	0x0145, 1048577,
-	0x0147, 1048577,
-	0x014a, 1048577,
-	0x014c, 1048577,
-	0x014e, 1048577,
-	0x0150, 1048577,
-	0x0152, 1048577,
-	0x0154, 1048577,
-	0x0156, 1048577,
-	0x0158, 1048577,
-	0x015a, 1048577,
-	0x015c, 1048577,
-	0x015e, 1048577,
-	0x0160, 1048577,
-	0x0162, 1048577,
-	0x0164, 1048577,
-	0x0166, 1048577,
-	0x0168, 1048577,
-	0x016a, 1048577,
-	0x016c, 1048577,
-	0x016e, 1048577,
-	0x0170, 1048577,
-	0x0172, 1048577,
-	0x0174, 1048577,
-	0x0176, 1048577,
-	0x0178, 1048455,
-	0x0179, 1048577,
-	0x017b, 1048577,
-	0x017d, 1048577,
-	0x0181, 1048786,
-	0x0182, 1048577,
-	0x0184, 1048577,
-	0x0186, 1048782,
-	0x0187, 1048577,
-	0x018b, 1048577,
-	0x018e, 1048655,
-	0x018f, 1048778,
-	0x0190, 1048779,
-	0x0191, 1048577,
-	0x0193, 1048781,
-	0x0194, 1048783,
-	0x0196, 1048787,
-	0x0197, 1048785,
-	0x0198, 1048577,
-	0x019c, 1048787,
-	0x019d, 1048789,
-	0x019f, 1048790,
-	0x01a0, 1048577,
-	0x01a2, 1048577,
-	0x01a4, 1048577,
-	0x01a6, 1048794,
-	0x01a7, 1048577,
-	0x01a9, 1048794,
-	0x01ac, 1048577,
-	0x01ae, 1048794,
-	0x01af, 1048577,
-	0x01b3, 1048577,
-	0x01b5, 1048577,
-	0x01b7, 1048795,
-	0x01b8, 1048577,
-	0x01bc, 1048577,
-	0x01c4, 1048578,
-	0x01c5, 1048577,
-	0x01c7, 1048578,
-	0x01c8, 1048577,
-	0x01ca, 1048578,
-	0x01cb, 1048577,
-	0x01cd, 1048577,
-	0x01cf, 1048577,
-	0x01d1, 1048577,
-	0x01d3, 1048577,
-	0x01d5, 1048577,
-	0x01d7, 1048577,
-	0x01d9, 1048577,
-	0x01db, 1048577,
-	0x01de, 1048577,
-	0x01e0, 1048577,
-	0x01e2, 1048577,
-	0x01e4, 1048577,
-	0x01e6, 1048577,
-	0x01e8, 1048577,
-	0x01ea, 1048577,
-	0x01ec, 1048577,
-	0x01ee, 1048577,
-	0x01f1, 1048578,
-	0x01f2, 1048577,
-	0x01f4, 1048577,
-	0x01f6, 1048479,
-	0x01f7, 1048520,
-	0x01f8, 1048577,
-	0x01fa, 1048577,
-	0x01fc, 1048577,
-	0x01fe, 1048577,
-	0x0200, 1048577,
-	0x0202, 1048577,
-	0x0204, 1048577,
-	0x0206, 1048577,
-	0x0208, 1048577,
-	0x020a, 1048577,
-	0x020c, 1048577,
-	0x020e, 1048577,
-	0x0210, 1048577,
-	0x0212, 1048577,
-	0x0214, 1048577,
-	0x0216, 1048577,
-	0x0218, 1048577,
-	0x021a, 1048577,
-	0x021c, 1048577,
-	0x021e, 1048577,
-	0x0220, 1048446,
-	0x0222, 1048577,
-	0x0224, 1048577,
-	0x0226, 1048577,
-	0x0228, 1048577,
-	0x022a, 1048577,
-	0x022c, 1048577,
-	0x022e, 1048577,
-	0x0230, 1048577,
-	0x0232, 1048577,
-	0x023a, 1059371,
-	0x023b, 1048577,
-	0x023d, 1048413,
-	0x023e, 1059368,
-	0x0241, 1048577,
-	0x0243, 1048381,
-	0x0244, 1048645,
-	0x0245, 1048647,
-	0x0246, 1048577,
-	0x0248, 1048577,
-	0x024a, 1048577,
-	0x024c, 1048577,
-	0x024e, 1048577,
-	0x0370, 1048577,
-	0x0372, 1048577,
-	0x0376, 1048577,
-	0x037f, 1048692,
-	0x0386, 1048614,
-	0x038c, 1048640,
-	0x03cf, 1048584,
-	0x03d8, 1048577,
-	0x03da, 1048577,
-	0x03dc, 1048577,
-	0x03de, 1048577,
-	0x03e0, 1048577,
-	0x03e2, 1048577,
-	0x03e4, 1048577,
-	0x03e6, 1048577,
-	0x03e8, 1048577,
-	0x03ea, 1048577,
-	0x03ec, 1048577,
-	0x03ee, 1048577,
-	0x03f4, 1048516,
-	0x03f7, 1048577,
-	0x03f9, 1048569,
-	0x03fa, 1048577,
-	0x0460, 1048577,
-	0x0462, 1048577,
-	0x0464, 1048577,
-	0x0466, 1048577,
-	0x0468, 1048577,
-	0x046a, 1048577,
-	0x046c, 1048577,
-	0x046e, 1048577,
-	0x0470, 1048577,
-	0x0472, 1048577,
-	0x0474, 1048577,
-	0x0476, 1048577,
-	0x0478, 1048577,
-	0x047a, 1048577,
-	0x047c, 1048577,
-	0x047e, 1048577,
-	0x0480, 1048577,
-	0x048a, 1048577,
-	0x048c, 1048577,
-	0x048e, 1048577,
-	0x0490, 1048577,
-	0x0492, 1048577,
-	0x0494, 1048577,
-	0x0496, 1048577,
-	0x0498, 1048577,
-	0x049a, 1048577,
-	0x049c, 1048577,
-	0x049e, 1048577,
-	0x04a0, 1048577,
-	0x04a2, 1048577,
-	0x04a4, 1048577,
-	0x04a6, 1048577,
-	0x04a8, 1048577,
-	0x04aa, 1048577,
-	0x04ac, 1048577,
-	0x04ae, 1048577,
-	0x04b0, 1048577,
-	0x04b2, 1048577,
-	0x04b4, 1048577,
-	0x04b6, 1048577,
-	0x04b8, 1048577,
-	0x04ba, 1048577,
-	0x04bc, 1048577,
-	0x04be, 1048577,
-	0x04c0, 1048591,
-	0x04c1, 1048577,
-	0x04c3, 1048577,
-	0x04c5, 1048577,
-	0x04c7, 1048577,
-	0x04c9, 1048577,
-	0x04cb, 1048577,
-	0x04cd, 1048577,
-	0x04d0, 1048577,
-	0x04d2, 1048577,
-	0x04d4, 1048577,
-	0x04d6, 1048577,
-	0x04d8, 1048577,
-	0x04da, 1048577,
-	0x04dc, 1048577,
-	0x04de, 1048577,
-	0x04e0, 1048577,
-	0x04e2, 1048577,
-	0x04e4, 1048577,
-	0x04e6, 1048577,
-	0x04e8, 1048577,
-	0x04ea, 1048577,
-	0x04ec, 1048577,
-	0x04ee, 1048577,
-	0x04f0, 1048577,
-	0x04f2, 1048577,
-	0x04f4, 1048577,
-	0x04f6, 1048577,
-	0x04f8, 1048577,
-	0x04fa, 1048577,
-	0x04fc, 1048577,
-	0x04fe, 1048577,
-	0x0500, 1048577,
-	0x0502, 1048577,
-	0x0504, 1048577,
-	0x0506, 1048577,
-	0x0508, 1048577,
-	0x050a, 1048577,
-	0x050c, 1048577,
-	0x050e, 1048577,
-	0x0510, 1048577,
-	0x0512, 1048577,
-	0x0514, 1048577,
-	0x0516, 1048577,
-	0x0518, 1048577,
-	0x051a, 1048577,
-	0x051c, 1048577,
-	0x051e, 1048577,
-	0x0520, 1048577,
-	0x0522, 1048577,
-	0x0524, 1048577,
-	0x0526, 1048577,
-	0x0528, 1048577,
-	0x052a, 1048577,
-	0x052c, 1048577,
-	0x052e, 1048577,
-	0x10c7, 1055840,
-	0x10cd, 1055840,
-	0x1e00, 1048577,
-	0x1e02, 1048577,
-	0x1e04, 1048577,
-	0x1e06, 1048577,
-	0x1e08, 1048577,
-	0x1e0a, 1048577,
-	0x1e0c, 1048577,
-	0x1e0e, 1048577,
-	0x1e10, 1048577,
-	0x1e12, 1048577,
-	0x1e14, 1048577,
-	0x1e16, 1048577,
-	0x1e18, 1048577,
-	0x1e1a, 1048577,
-	0x1e1c, 1048577,
-	0x1e1e, 1048577,
-	0x1e20, 1048577,
-	0x1e22, 1048577,
-	0x1e24, 1048577,
-	0x1e26, 1048577,
-	0x1e28, 1048577,
-	0x1e2a, 1048577,
-	0x1e2c, 1048577,
-	0x1e2e, 1048577,
-	0x1e30, 1048577,
-	0x1e32, 1048577,
-	0x1e34, 1048577,
-	0x1e36, 1048577,
-	0x1e38, 1048577,
-	0x1e3a, 1048577,
-	0x1e3c, 1048577,
-	0x1e3e, 1048577,
-	0x1e40, 1048577,
-	0x1e42, 1048577,
-	0x1e44, 1048577,
-	0x1e46, 1048577,
-	0x1e48, 1048577,
-	0x1e4a, 1048577,
-	0x1e4c, 1048577,
-	0x1e4e, 1048577,
-	0x1e50, 1048577,
-	0x1e52, 1048577,
-	0x1e54, 1048577,
-	0x1e56, 1048577,
-	0x1e58, 1048577,
-	0x1e5a, 1048577,
-	0x1e5c, 1048577,
-	0x1e5e, 1048577,
-	0x1e60, 1048577,
-	0x1e62, 1048577,
-	0x1e64, 1048577,
-	0x1e66, 1048577,
-	0x1e68, 1048577,
-	0x1e6a, 1048577,
-	0x1e6c, 1048577,
-	0x1e6e, 1048577,
-	0x1e70, 1048577,
-	0x1e72, 1048577,
-	0x1e74, 1048577,
-	0x1e76, 1048577,
-	0x1e78, 1048577,
-	0x1e7a, 1048577,
-	0x1e7c, 1048577,
-	0x1e7e, 1048577,
-	0x1e80, 1048577,
-	0x1e82, 1048577,
-	0x1e84, 1048577,
-	0x1e86, 1048577,
-	0x1e88, 1048577,
-	0x1e8a, 1048577,
-	0x1e8c, 1048577,
-	0x1e8e, 1048577,
-	0x1e90, 1048577,
-	0x1e92, 1048577,
-	0x1e94, 1048577,
-	0x1e9e, 1040961,
-	0x1ea0, 1048577,
-	0x1ea2, 1048577,
-	0x1ea4, 1048577,
-	0x1ea6, 1048577,
-	0x1ea8, 1048577,
-	0x1eaa, 1048577,
-	0x1eac, 1048577,
-	0x1eae, 1048577,
-	0x1eb0, 1048577,
-	0x1eb2, 1048577,
-	0x1eb4, 1048577,
-	0x1eb6, 1048577,
-	0x1eb8, 1048577,
-	0x1eba, 1048577,
-	0x1ebc, 1048577,
-	0x1ebe, 1048577,
-	0x1ec0, 1048577,
-	0x1ec2, 1048577,
-	0x1ec4, 1048577,
-	0x1ec6, 1048577,
-	0x1ec8, 1048577,
-	0x1eca, 1048577,
-	0x1ecc, 1048577,
-	0x1ece, 1048577,
-	0x1ed0, 1048577,
-	0x1ed2, 1048577,
-	0x1ed4, 1048577,
-	0x1ed6, 1048577,
-	0x1ed8, 1048577,
-	0x1eda, 1048577,
-	0x1edc, 1048577,
-	0x1ede, 1048577,
-	0x1ee0, 1048577,
-	0x1ee2, 1048577,
-	0x1ee4, 1048577,
-	0x1ee6, 1048577,
-	0x1ee8, 1048577,
-	0x1eea, 1048577,
-	0x1eec, 1048577,
-	0x1eee, 1048577,
-	0x1ef0, 1048577,
-	0x1ef2, 1048577,
-	0x1ef4, 1048577,
-	0x1ef6, 1048577,
-	0x1ef8, 1048577,
-	0x1efa, 1048577,
-	0x1efc, 1048577,
-	0x1efe, 1048577,
-	0x1f59, 1048568,
-	0x1f5b, 1048568,
-	0x1f5d, 1048568,
-	0x1f5f, 1048568,
-	0x1fbc, 1048567,
-	0x1fcc, 1048567,
-	0x1fec, 1048569,
-	0x1ffc, 1048567,
-	0x2126, 1041059,
-	0x212a, 1040193,
-	0x212b, 1040314,
-	0x2132, 1048604,
-	0x2183, 1048577,
-	0x2c60, 1048577,
-	0x2c62, 1037833,
-	0x2c63, 1044762,
-	0x2c64, 1037849,
-	0x2c67, 1048577,
-	0x2c69, 1048577,
-	0x2c6b, 1048577,
-	0x2c6d, 1037796,
-	0x2c6e, 1037827,
-	0x2c6f, 1037793,
-	0x2c70, 1037794,
-	0x2c72, 1048577,
-	0x2c75, 1048577,
-	0x2c80, 1048577,
-	0x2c82, 1048577,
-	0x2c84, 1048577,
-	0x2c86, 1048577,
-	0x2c88, 1048577,
-	0x2c8a, 1048577,
-	0x2c8c, 1048577,
-	0x2c8e, 1048577,
-	0x2c90, 1048577,
-	0x2c92, 1048577,
-	0x2c94, 1048577,
-	0x2c96, 1048577,
-	0x2c98, 1048577,
-	0x2c9a, 1048577,
-	0x2c9c, 1048577,
-	0x2c9e, 1048577,
-	0x2ca0, 1048577,
-	0x2ca2, 1048577,
-	0x2ca4, 1048577,
-	0x2ca6, 1048577,
-	0x2ca8, 1048577,
-	0x2caa, 1048577,
-	0x2cac, 1048577,
-	0x2cae, 1048577,
-	0x2cb0, 1048577,
-	0x2cb2, 1048577,
-	0x2cb4, 1048577,
-	0x2cb6, 1048577,
-	0x2cb8, 1048577,
-	0x2cba, 1048577,
-	0x2cbc, 1048577,
-	0x2cbe, 1048577,
-	0x2cc0, 1048577,
-	0x2cc2, 1048577,
-	0x2cc4, 1048577,
-	0x2cc6, 1048577,
-	0x2cc8, 1048577,
-	0x2cca, 1048577,
-	0x2ccc, 1048577,
-	0x2cce, 1048577,
-	0x2cd0, 1048577,
-	0x2cd2, 1048577,
-	0x2cd4, 1048577,
-	0x2cd6, 1048577,
-	0x2cd8, 1048577,
-	0x2cda, 1048577,
-	0x2cdc, 1048577,
-	0x2cde, 1048577,
-	0x2ce0, 1048577,
-	0x2ce2, 1048577,
-	0x2ceb, 1048577,
-	0x2ced, 1048577,
-	0x2cf2, 1048577,
-	0xa640, 1048577,
-	0xa642, 1048577,
-	0xa644, 1048577,
-	0xa646, 1048577,
-	0xa648, 1048577,
-	0xa64a, 1048577,
-	0xa64c, 1048577,
-	0xa64e, 1048577,
-	0xa650, 1048577,
-	0xa652, 1048577,
-	0xa654, 1048577,
-	0xa656, 1048577,
-	0xa658, 1048577,
-	0xa65a, 1048577,
-	0xa65c, 1048577,
-	0xa65e, 1048577,
-	0xa660, 1048577,
-	0xa662, 1048577,
-	0xa664, 1048577,
-	0xa666, 1048577,
-	0xa668, 1048577,
-	0xa66a, 1048577,
-	0xa66c, 1048577,
-	0xa680, 1048577,
-	0xa682, 1048577,
-	0xa684, 1048577,
-	0xa686, 1048577,
-	0xa688, 1048577,
-	0xa68a, 1048577,
-	0xa68c, 1048577,
-	0xa68e, 1048577,
-	0xa690, 1048577,
-	0xa692, 1048577,
-	0xa694, 1048577,
-	0xa696, 1048577,
-	0xa698, 1048577,
-	0xa69a, 1048577,
-	0xa722, 1048577,
-	0xa724, 1048577,
-	0xa726, 1048577,
-	0xa728, 1048577,
-	0xa72a, 1048577,
-	0xa72c, 1048577,
-	0xa72e, 1048577,
-	0xa732, 1048577,
-	0xa734, 1048577,
-	0xa736, 1048577,
-	0xa738, 1048577,
-	0xa73a, 1048577,
-	0xa73c, 1048577,
-	0xa73e, 1048577,
-	0xa740, 1048577,
-	0xa742, 1048577,
-	0xa744, 1048577,
-	0xa746, 1048577,
-	0xa748, 1048577,
-	0xa74a, 1048577,
-	0xa74c, 1048577,
-	0xa74e, 1048577,
-	0xa750, 1048577,
-	0xa752, 1048577,
-	0xa754, 1048577,
-	0xa756, 1048577,
-	0xa758, 1048577,
-	0xa75a, 1048577,
-	0xa75c, 1048577,
-	0xa75e, 1048577,
-	0xa760, 1048577,
-	0xa762, 1048577,
-	0xa764, 1048577,
-	0xa766, 1048577,
-	0xa768, 1048577,
-	0xa76a, 1048577,
-	0xa76c, 1048577,
-	0xa76e, 1048577,
-	0xa779, 1048577,
-	0xa77b, 1048577,
-	0xa77d, 1013244,
-	0xa77e, 1048577,
-	0xa780, 1048577,
-	0xa782, 1048577,
-	0xa784, 1048577,
-	0xa786, 1048577,
-	0xa78b, 1048577,
-	0xa78d, 1006296,
-	0xa790, 1048577,
-	0xa792, 1048577,
-	0xa796, 1048577,
-	0xa798, 1048577,
-	0xa79a, 1048577,
-	0xa79c, 1048577,
-	0xa79e, 1048577,
-	0xa7a0, 1048577,
-	0xa7a2, 1048577,
-	0xa7a4, 1048577,
-	0xa7a6, 1048577,
-	0xa7a8, 1048577,
-	0xa7aa, 1006268,
-	0xa7ab, 1006257,
-	0xa7ac, 1006261,
-	0xa7ad, 1006271,
-	0xa7b0, 1006318,
-	0xa7b1, 1006294,
-};
-
-char32_t tolower(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, tolowerr, nelem (tolowerr)/3, 3);
-	if (p && c >= p[0] && c <= p[1])
-		return c + p[2] - 1048576;
-
-	p = rbsearch(c, tolowers, nelem (tolowers)/2, 2);
-	if (p && c == p[0])
-		return c + p[1] - 1048576;
-
-	return c;
-}
-
-char32_t totitler[] = {
-	0x0061, 0x007a, 1048544,
-	0x00e0, 0x00f6, 1048544,
-	0x00f8, 0x00fe, 1048544,
-	0x023f, 0x0240, 1059391,
-	0x0256, 0x0257, 1048371,
-	0x028a, 0x028b, 1048359,
-	0x037b, 0x037d, 1048706,
-	0x03ad, 0x03af, 1048539,
-	0x03b1, 0x03c1, 1048544,
-	0x03c3, 0x03cb, 1048544,
-	0x03cd, 0x03ce, 1048513,
-	0x0430, 0x044f, 1048544,
-	0x0450, 0x045f, 1048496,
-	0x0561, 0x0586, 1048528,
-	0x1f00, 0x1f07, 1048584,
-	0x1f10, 0x1f15, 1048584,
-	0x1f20, 0x1f27, 1048584,
-	0x1f30, 0x1f37, 1048584,
-	0x1f40, 0x1f45, 1048584,
-	0x1f60, 0x1f67, 1048584,
-	0x1f70, 0x1f71, 1048650,
-	0x1f72, 0x1f75, 1048662,
-	0x1f76, 0x1f77, 1048676,
-	0x1f78, 0x1f79, 1048704,
-	0x1f7a, 0x1f7b, 1048688,
-	0x1f7c, 0x1f7d, 1048702,
-	0x1f80, 0x1f87, 1048584,
-	0x1f90, 0x1f97, 1048584,
-	0x1fa0, 0x1fa7, 1048584,
-	0x1fb0, 0x1fb1, 1048584,
-	0x1fd0, 0x1fd1, 1048584,
-	0x1fe0, 0x1fe1, 1048584,
-	0x2170, 0x217f, 1048560,
-	0x24d0, 0x24e9, 1048550,
-	0x2c30, 0x2c5e, 1048528,
-	0x2d00, 0x2d25, 1041312,
-	0xff41, 0xff5a, 1048544,
-	0x10428, 0x1044f, 1048536,
-	0x118c0, 0x118df, 1048544,
-};
-
-static char32_t totitles[] = {
-	0x00b5, 1049319,
-	0x00ff, 1048697,
-	0x0101, 1048575,
-	0x0103, 1048575,
-	0x0105, 1048575,
-	0x0107, 1048575,
-	0x0109, 1048575,
-	0x010b, 1048575,
-	0x010d, 1048575,
-	0x010f, 1048575,
-	0x0111, 1048575,
-	0x0113, 1048575,
-	0x0115, 1048575,
-	0x0117, 1048575,
-	0x0119, 1048575,
-	0x011b, 1048575,
-	0x011d, 1048575,
-	0x011f, 1048575,
-	0x0121, 1048575,
-	0x0123, 1048575,
-	0x0125, 1048575,
-	0x0127, 1048575,
-	0x0129, 1048575,
-	0x012b, 1048575,
-	0x012d, 1048575,
-	0x012f, 1048575,
-	0x0131, 1048344,
-	0x0133, 1048575,
-	0x0135, 1048575,
-	0x0137, 1048575,
-	0x013a, 1048575,
-	0x013c, 1048575,
-	0x013e, 1048575,
-	0x0140, 1048575,
-	0x0142, 1048575,
-	0x0144, 1048575,
-	0x0146, 1048575,
-	0x0148, 1048575,
-	0x014b, 1048575,
-	0x014d, 1048575,
-	0x014f, 1048575,
-	0x0151, 1048575,
-	0x0153, 1048575,
-	0x0155, 1048575,
-	0x0157, 1048575,
-	0x0159, 1048575,
-	0x015b, 1048575,
-	0x015d, 1048575,
-	0x015f, 1048575,
-	0x0161, 1048575,
-	0x0163, 1048575,
-	0x0165, 1048575,
-	0x0167, 1048575,
-	0x0169, 1048575,
-	0x016b, 1048575,
-	0x016d, 1048575,
-	0x016f, 1048575,
-	0x0171, 1048575,
-	0x0173, 1048575,
-	0x0175, 1048575,
-	0x0177, 1048575,
-	0x017a, 1048575,
-	0x017c, 1048575,
-	0x017e, 1048575,
-	0x017f, 1048276,
-	0x0180, 1048771,
-	0x0183, 1048575,
-	0x0185, 1048575,
-	0x0188, 1048575,
-	0x018c, 1048575,
-	0x0192, 1048575,
-	0x0195, 1048673,
-	0x0199, 1048575,
-	0x019a, 1048739,
-	0x019e, 1048706,
-	0x01a1, 1048575,
-	0x01a3, 1048575,
-	0x01a5, 1048575,
-	0x01a8, 1048575,
-	0x01ad, 1048575,
-	0x01b0, 1048575,
-	0x01b4, 1048575,
-	0x01b6, 1048575,
-	0x01b9, 1048575,
-	0x01bd, 1048575,
-	0x01bf, 1048632,
-	0x01c4, 1048577,
-	0x01c6, 1048575,
-	0x01c7, 1048577,
-	0x01c9, 1048575,
-	0x01ca, 1048577,
-	0x01cc, 1048575,
-	0x01ce, 1048575,
-	0x01d0, 1048575,
-	0x01d2, 1048575,
-	0x01d4, 1048575,
-	0x01d6, 1048575,
-	0x01d8, 1048575,
-	0x01da, 1048575,
-	0x01dc, 1048575,
-	0x01dd, 1048497,
-	0x01df, 1048575,
-	0x01e1, 1048575,
-	0x01e3, 1048575,
-	0x01e5, 1048575,
-	0x01e7, 1048575,
-	0x01e9, 1048575,
-	0x01eb, 1048575,
-	0x01ed, 1048575,
-	0x01ef, 1048575,
-	0x01f1, 1048577,
-	0x01f3, 1048575,
-	0x01f5, 1048575,
-	0x01f9, 1048575,
-	0x01fb, 1048575,
-	0x01fd, 1048575,
-	0x01ff, 1048575,
-	0x0201, 1048575,
-	0x0203, 1048575,
-	0x0205, 1048575,
-	0x0207, 1048575,
-	0x0209, 1048575,
-	0x020b, 1048575,
-	0x020d, 1048575,
-	0x020f, 1048575,
-	0x0211, 1048575,
-	0x0213, 1048575,
-	0x0215, 1048575,
-	0x0217, 1048575,
-	0x0219, 1048575,
-	0x021b, 1048575,
-	0x021d, 1048575,
-	0x021f, 1048575,
-	0x0223, 1048575,
-	0x0225, 1048575,
-	0x0227, 1048575,
-	0x0229, 1048575,
-	0x022b, 1048575,
-	0x022d, 1048575,
-	0x022f, 1048575,
-	0x0231, 1048575,
-	0x0233, 1048575,
-	0x023c, 1048575,
-	0x0242, 1048575,
-	0x0247, 1048575,
-	0x0249, 1048575,
-	0x024b, 1048575,
-	0x024d, 1048575,
-	0x024f, 1048575,
-	0x0250, 1059359,
-	0x0251, 1059356,
-	0x0252, 1059358,
-	0x0253, 1048366,
-	0x0254, 1048370,
-	0x0259, 1048374,
-	0x025b, 1048373,
-	0x025c, 1090895,
-	0x0260, 1048371,
-	0x0261, 1090891,
-	0x0263, 1048369,
-	0x0265, 1090856,
-	0x0266, 1090884,
-	0x0268, 1048367,
-	0x0269, 1048365,
-	0x026b, 1059319,
-	0x026c, 1090881,
-	0x026f, 1048365,
-	0x0271, 1059325,
-	0x0272, 1048363,
-	0x0275, 1048362,
-	0x027d, 1059303,
-	0x0280, 1048358,
-	0x0283, 1048358,
-	0x0287, 1090858,
-	0x0288, 1048358,
-	0x0289, 1048507,
-	0x028c, 1048505,
-	0x0292, 1048357,
-	0x029e, 1090834,
-	0x0345, 1048660,
-	0x0371, 1048575,
-	0x0373, 1048575,
-	0x0377, 1048575,
-	0x03ac, 1048538,
-	0x03c2, 1048545,
-	0x03cc, 1048512,
-	0x03d0, 1048514,
-	0x03d1, 1048519,
-	0x03d5, 1048529,
-	0x03d6, 1048522,
-	0x03d7, 1048568,
-	0x03d9, 1048575,
-	0x03db, 1048575,
-	0x03dd, 1048575,
-	0x03df, 1048575,
-	0x03e1, 1048575,
-	0x03e3, 1048575,
-	0x03e5, 1048575,
-	0x03e7, 1048575,
-	0x03e9, 1048575,
-	0x03eb, 1048575,
-	0x03ed, 1048575,
-	0x03ef, 1048575,
-	0x03f0, 1048490,
-	0x03f1, 1048496,
-	0x03f2, 1048583,
-	0x03f3, 1048460,
-	0x03f5, 1048480,
-	0x03f8, 1048575,
-	0x03fb, 1048575,
-	0x0461, 1048575,
-	0x0463, 1048575,
-	0x0465, 1048575,
-	0x0467, 1048575,
-	0x0469, 1048575,
-	0x046b, 1048575,
-	0x046d, 1048575,
-	0x046f, 1048575,
-	0x0471, 1048575,
-	0x0473, 1048575,
-	0x0475, 1048575,
-	0x0477, 1048575,
-	0x0479, 1048575,
-	0x047b, 1048575,
-	0x047d, 1048575,
-	0x047f, 1048575,
-	0x0481, 1048575,
-	0x048b, 1048575,
-	0x048d, 1048575,
-	0x048f, 1048575,
-	0x0491, 1048575,
-	0x0493, 1048575,
-	0x0495, 1048575,
-	0x0497, 1048575,
-	0x0499, 1048575,
-	0x049b, 1048575,
-	0x049d, 1048575,
-	0x049f, 1048575,
-	0x04a1, 1048575,
-	0x04a3, 1048575,
-	0x04a5, 1048575,
-	0x04a7, 1048575,
-	0x04a9, 1048575,
-	0x04ab, 1048575,
-	0x04ad, 1048575,
-	0x04af, 1048575,
-	0x04b1, 1048575,
-	0x04b3, 1048575,
-	0x04b5, 1048575,
-	0x04b7, 1048575,
-	0x04b9, 1048575,
-	0x04bb, 1048575,
-	0x04bd, 1048575,
-	0x04bf, 1048575,
-	0x04c2, 1048575,
-	0x04c4, 1048575,
-	0x04c6, 1048575,
-	0x04c8, 1048575,
-	0x04ca, 1048575,
-	0x04cc, 1048575,
-	0x04ce, 1048575,
-	0x04cf, 1048561,
-	0x04d1, 1048575,
-	0x04d3, 1048575,
-	0x04d5, 1048575,
-	0x04d7, 1048575,
-	0x04d9, 1048575,
-	0x04db, 1048575,
-	0x04dd, 1048575,
-	0x04df, 1048575,
-	0x04e1, 1048575,
-	0x04e3, 1048575,
-	0x04e5, 1048575,
-	0x04e7, 1048575,
-	0x04e9, 1048575,
-	0x04eb, 1048575,
-	0x04ed, 1048575,
-	0x04ef, 1048575,
-	0x04f1, 1048575,
-	0x04f3, 1048575,
-	0x04f5, 1048575,
-	0x04f7, 1048575,
-	0x04f9, 1048575,
-	0x04fb, 1048575,
-	0x04fd, 1048575,
-	0x04ff, 1048575,
-	0x0501, 1048575,
-	0x0503, 1048575,
-	0x0505, 1048575,
-	0x0507, 1048575,
-	0x0509, 1048575,
-	0x050b, 1048575,
-	0x050d, 1048575,
-	0x050f, 1048575,
-	0x0511, 1048575,
-	0x0513, 1048575,
-	0x0515, 1048575,
-	0x0517, 1048575,
-	0x0519, 1048575,
-	0x051b, 1048575,
-	0x051d, 1048575,
-	0x051f, 1048575,
-	0x0521, 1048575,
-	0x0523, 1048575,
-	0x0525, 1048575,
-	0x0527, 1048575,
-	0x0529, 1048575,
-	0x052b, 1048575,
-	0x052d, 1048575,
-	0x052f, 1048575,
-	0x1d79, 1083908,
-	0x1d7d, 1052390,
-	0x1e01, 1048575,
-	0x1e03, 1048575,
-	0x1e05, 1048575,
-	0x1e07, 1048575,
-	0x1e09, 1048575,
-	0x1e0b, 1048575,
-	0x1e0d, 1048575,
-	0x1e0f, 1048575,
-	0x1e11, 1048575,
-	0x1e13, 1048575,
-	0x1e15, 1048575,
-	0x1e17, 1048575,
-	0x1e19, 1048575,
-	0x1e1b, 1048575,
-	0x1e1d, 1048575,
-	0x1e1f, 1048575,
-	0x1e21, 1048575,
-	0x1e23, 1048575,
-	0x1e25, 1048575,
-	0x1e27, 1048575,
-	0x1e29, 1048575,
-	0x1e2b, 1048575,
-	0x1e2d, 1048575,
-	0x1e2f, 1048575,
-	0x1e31, 1048575,
-	0x1e33, 1048575,
-	0x1e35, 1048575,
-	0x1e37, 1048575,
-	0x1e39, 1048575,
-	0x1e3b, 1048575,
-	0x1e3d, 1048575,
-	0x1e3f, 1048575,
-	0x1e41, 1048575,
-	0x1e43, 1048575,
-	0x1e45, 1048575,
-	0x1e47, 1048575,
-	0x1e49, 1048575,
-	0x1e4b, 1048575,
-	0x1e4d, 1048575,
-	0x1e4f, 1048575,
-	0x1e51, 1048575,
-	0x1e53, 1048575,
-	0x1e55, 1048575,
-	0x1e57, 1048575,
-	0x1e59, 1048575,
-	0x1e5b, 1048575,
-	0x1e5d, 1048575,
-	0x1e5f, 1048575,
-	0x1e61, 1048575,
-	0x1e63, 1048575,
-	0x1e65, 1048575,
-	0x1e67, 1048575,
-	0x1e69, 1048575,
-	0x1e6b, 1048575,
-	0x1e6d, 1048575,
-	0x1e6f, 1048575,
-	0x1e71, 1048575,
-	0x1e73, 1048575,
-	0x1e75, 1048575,
-	0x1e77, 1048575,
-	0x1e79, 1048575,
-	0x1e7b, 1048575,
-	0x1e7d, 1048575,
-	0x1e7f, 1048575,
-	0x1e81, 1048575,
-	0x1e83, 1048575,
-	0x1e85, 1048575,
-	0x1e87, 1048575,
-	0x1e89, 1048575,
-	0x1e8b, 1048575,
-	0x1e8d, 1048575,
-	0x1e8f, 1048575,
-	0x1e91, 1048575,
-	0x1e93, 1048575,
-	0x1e95, 1048575,
-	0x1e9b, 1048517,
-	0x1ea1, 1048575,
-	0x1ea3, 1048575,
-	0x1ea5, 1048575,
-	0x1ea7, 1048575,
-	0x1ea9, 1048575,
-	0x1eab, 1048575,
-	0x1ead, 1048575,
-	0x1eaf, 1048575,
-	0x1eb1, 1048575,
-	0x1eb3, 1048575,
-	0x1eb5, 1048575,
-	0x1eb7, 1048575,
-	0x1eb9, 1048575,
-	0x1ebb, 1048575,
-	0x1ebd, 1048575,
-	0x1ebf, 1048575,
-	0x1ec1, 1048575,
-	0x1ec3, 1048575,
-	0x1ec5, 1048575,
-	0x1ec7, 1048575,
-	0x1ec9, 1048575,
-	0x1ecb, 1048575,
-	0x1ecd, 1048575,
-	0x1ecf, 1048575,
-	0x1ed1, 1048575,
-	0x1ed3, 1048575,
-	0x1ed5, 1048575,
-	0x1ed7, 1048575,
-	0x1ed9, 1048575,
-	0x1edb, 1048575,
-	0x1edd, 1048575,
-	0x1edf, 1048575,
-	0x1ee1, 1048575,
-	0x1ee3, 1048575,
-	0x1ee5, 1048575,
-	0x1ee7, 1048575,
-	0x1ee9, 1048575,
-	0x1eeb, 1048575,
-	0x1eed, 1048575,
-	0x1eef, 1048575,
-	0x1ef1, 1048575,
-	0x1ef3, 1048575,
-	0x1ef5, 1048575,
-	0x1ef7, 1048575,
-	0x1ef9, 1048575,
-	0x1efb, 1048575,
-	0x1efd, 1048575,
-	0x1eff, 1048575,
-	0x1f51, 1048584,
-	0x1f53, 1048584,
-	0x1f55, 1048584,
-	0x1f57, 1048584,
-	0x1fb3, 1048585,
-	0x1fbe, 1041371,
-	0x1fc3, 1048585,
-	0x1fe5, 1048583,
-	0x1ff3, 1048585,
-	0x214e, 1048548,
-	0x2184, 1048575,
-	0x2c61, 1048575,
-	0x2c65, 1037781,
-	0x2c66, 1037784,
-	0x2c68, 1048575,
-	0x2c6a, 1048575,
-	0x2c6c, 1048575,
-	0x2c73, 1048575,
-	0x2c76, 1048575,
-	0x2c81, 1048575,
-	0x2c83, 1048575,
-	0x2c85, 1048575,
-	0x2c87, 1048575,
-	0x2c89, 1048575,
-	0x2c8b, 1048575,
-	0x2c8d, 1048575,
-	0x2c8f, 1048575,
-	0x2c91, 1048575,
-	0x2c93, 1048575,
-	0x2c95, 1048575,
-	0x2c97, 1048575,
-	0x2c99, 1048575,
-	0x2c9b, 1048575,
-	0x2c9d, 1048575,
-	0x2c9f, 1048575,
-	0x2ca1, 1048575,
-	0x2ca3, 1048575,
-	0x2ca5, 1048575,
-	0x2ca7, 1048575,
-	0x2ca9, 1048575,
-	0x2cab, 1048575,
-	0x2cad, 1048575,
-	0x2caf, 1048575,
-	0x2cb1, 1048575,
-	0x2cb3, 1048575,
-	0x2cb5, 1048575,
-	0x2cb7, 1048575,
-	0x2cb9, 1048575,
-	0x2cbb, 1048575,
-	0x2cbd, 1048575,
-	0x2cbf, 1048575,
-	0x2cc1, 1048575,
-	0x2cc3, 1048575,
-	0x2cc5, 1048575,
-	0x2cc7, 1048575,
-	0x2cc9, 1048575,
-	0x2ccb, 1048575,
-	0x2ccd, 1048575,
-	0x2ccf, 1048575,
-	0x2cd1, 1048575,
-	0x2cd3, 1048575,
-	0x2cd5, 1048575,
-	0x2cd7, 1048575,
-	0x2cd9, 1048575,
-	0x2cdb, 1048575,
-	0x2cdd, 1048575,
-	0x2cdf, 1048575,
-	0x2ce1, 1048575,
-	0x2ce3, 1048575,
-	0x2cec, 1048575,
-	0x2cee, 1048575,
-	0x2cf3, 1048575,
-	0x2d27, 1041312,
-	0x2d2d, 1041312,
-	0xa641, 1048575,
-	0xa643, 1048575,
-	0xa645, 1048575,
-	0xa647, 1048575,
-	0xa649, 1048575,
-	0xa64b, 1048575,
-	0xa64d, 1048575,
-	0xa64f, 1048575,
-	0xa651, 1048575,
-	0xa653, 1048575,
-	0xa655, 1048575,
-	0xa657, 1048575,
-	0xa659, 1048575,
-	0xa65b, 1048575,
-	0xa65d, 1048575,
-	0xa65f, 1048575,
-	0xa661, 1048575,
-	0xa663, 1048575,
-	0xa665, 1048575,
-	0xa667, 1048575,
-	0xa669, 1048575,
-	0xa66b, 1048575,
-	0xa66d, 1048575,
-	0xa681, 1048575,
-	0xa683, 1048575,
-	0xa685, 1048575,
-	0xa687, 1048575,
-	0xa689, 1048575,
-	0xa68b, 1048575,
-	0xa68d, 1048575,
-	0xa68f, 1048575,
-	0xa691, 1048575,
-	0xa693, 1048575,
-	0xa695, 1048575,
-	0xa697, 1048575,
-	0xa699, 1048575,
-	0xa69b, 1048575,
-	0xa723, 1048575,
-	0xa725, 1048575,
-	0xa727, 1048575,
-	0xa729, 1048575,
-	0xa72b, 1048575,
-	0xa72d, 1048575,
-	0xa72f, 1048575,
-	0xa733, 1048575,
-	0xa735, 1048575,
-	0xa737, 1048575,
-	0xa739, 1048575,
-	0xa73b, 1048575,
-	0xa73d, 1048575,
-	0xa73f, 1048575,
-	0xa741, 1048575,
-	0xa743, 1048575,
-	0xa745, 1048575,
-	0xa747, 1048575,
-	0xa749, 1048575,
-	0xa74b, 1048575,
-	0xa74d, 1048575,
-	0xa74f, 1048575,
-	0xa751, 1048575,
-	0xa753, 1048575,
-	0xa755, 1048575,
-	0xa757, 1048575,
-	0xa759, 1048575,
-	0xa75b, 1048575,
-	0xa75d, 1048575,
-	0xa75f, 1048575,
-	0xa761, 1048575,
-	0xa763, 1048575,
-	0xa765, 1048575,
-	0xa767, 1048575,
-	0xa769, 1048575,
-	0xa76b, 1048575,
-	0xa76d, 1048575,
-	0xa76f, 1048575,
-	0xa77a, 1048575,
-	0xa77c, 1048575,
-	0xa77f, 1048575,
-	0xa781, 1048575,
-	0xa783, 1048575,
-	0xa785, 1048575,
-	0xa787, 1048575,
-	0xa78c, 1048575,
-	0xa791, 1048575,
-	0xa793, 1048575,
-	0xa797, 1048575,
-	0xa799, 1048575,
-	0xa79b, 1048575,
-	0xa79d, 1048575,
-	0xa79f, 1048575,
-	0xa7a1, 1048575,
-	0xa7a3, 1048575,
-	0xa7a5, 1048575,
-	0xa7a7, 1048575,
-	0xa7a9, 1048575,
-};
-
-char32_t totitle(char32_t c) noexcept
-{
-	char32_t *p;
-
-	p = rbsearch(c, totitler, nelem (totitler)/3, 3);
-	if (p && c >= p[0] && c <= p[1])
-		return c + p[2] - 1048576;
-
-	p = rbsearch(c, totitles, nelem (totitles)/2, 2);
-	if (p && c == p[0])
-		return c + p[1] - 1048576;
-
-	return c;
-}
-
-void encode(char32_t c, char res[5]) noexcept
-{
-	switch (nbytesPoint(c)) {
-	case 1:
-		res[0] = c;
-		res[1] = '\0';
-		break;
-	case 2:
-		res[0] = 0xC0 | ((c >> 6)  & 0x1F);
-		res[1] = 0x80 | (c & 0x3F);
-		res[2] = '\0';
-		break;
-	case 3:
-		res[0] = 0xE0 | ((c >> 12) & 0xF );
-		res[1] = 0x80 | ((c >> 6)  & 0x3F);
-		res[2] = 0x80 | (c & 0x3F);
-		res[3] = '\0';
-		break;
-	case 4:
-		res[0] = 0xF0 | ((c >> 18) & 0x7 );
-		res[1] = 0x80 | ((c >> 12) & 0x3F);
-		res[2] = 0x80 | ((c >> 6)  & 0x3F);
-		res[3] = 0x80 | (c & 0x3F);
-		res[4] = '\0';
-		break;
-	default:
-		break;
-	}
-}
-
-void decode(char32_t &c, const char *res) noexcept
-{
-	c = 0;
-
-	switch (nbytesUtf8(res[0])) {
-	case 1:
-		c = res[0];
-		break;
-	case 2:
-		c =  (res[0] & 0x1f) << 6;
-		c |= (res[1] & 0x3f);
-		break;
-	case 3:
-		c =  (res[0] & 0x0f) << 12;
-		c |= (res[1] & 0x3f) << 6;
-		c |= (res[2] & 0x3f);
-		break;
-	case 4:
-		c =  (res[0] & 0x07) << 16;
-		c |= (res[1] & 0x3f) << 12;
-		c |= (res[2] & 0x3f) << 6;
-		c |= (res[3] & 0x3f);
-	default:
-		break;
-	}
-}
-
-int nbytesUtf8(char c) noexcept
-{
-	if ((c & 0xE0) == 0xC0)
-		return 2;
-	if ((c & 0xF0) == 0xE0)
-		return 3;
-	if ((c & 0xF8) == 0xF0)
-		return 4;
-
-	return 1;
-}
-
-int nbytesPoint(char32_t c) noexcept
-{
-	if (c <= 0x7F)
-		return 1;
-	if (c <= 0x7FF)
-		return 2;
-	if (c <= 0xFFFF)
-		return 3;
-	if (c <= 0x1FFFFF)
-		return 4;
-
-	return -1;
-}
-
-int length(const std::string &str)
-{
-	int total = 0;
-
-	forEach(str, [&] (char32_t) {
-		++ total;
-	});
-
-	return total;
-}
-
-std::string toUtf8(const std::u32string &array)
-{
-	std::string res;
-
-	for (size_t i = 0; i < array.size(); ++i) {
-		char tmp[5];
-		int size = nbytesPoint(array[i]);
-
-		if (size < 0) {
-			throw std::invalid_argument("invalid sequence");
-		}
-
-		encode(array[i], tmp);
-		res.insert(res.length(), tmp);
-	}
-
-	return res;
-}
-
-std::u32string toUtf32(const std::string &str)
-{
-	std::u32string res;
-
-	forEach(str, [&] (char32_t code) {
-		res.push_back(code);
-	});
-
-	return res;
-}
-
-} // !unicode
-
-} // !irccd
--- a/common/unicode.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/*
- * unicode.h -- UTF-8 to UTF-32 conversions and various operations
- *
- * 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_UNICODE_H_
-#define _IRCCD_UNICODE_H_
-
-/**
- * @file Unicode.h
- * @brief UTF-8 to UTF-32 conversions
- */
-
-#include <stdexcept>
-#include <string>
-
-namespace irccd {
-
-namespace unicode {
-
-void encode(char32_t point, char res[5]) noexcept;
-void decode(char32_t &c, const char *res) noexcept;
-
-/**
- * Get the number of bytes for the first multi byte character from a
- * utf-8 string.
- *
- * This can be used to iterate a valid UTF-8 string to jump to the next
- * real character.
- *
- * @param c the first multi byte character
- * @return the number of bytes [1-4]
- */
-int nbytesUtf8(char c) noexcept;
-
-/**
- * Get the number of bytes for the unicode point.
- *
- * @param point the unicode point
- * @return the number of bytes [1-4] or -1 on invalid
- */
-int nbytesPoint(char32_t point) noexcept;
-
-/**
- * Get real number of character in a string.
- *
- * @param str the string
- * @return the length
- * @throw std::invalid_argument on invalid sequence
- */
-int length(const std::string &str);
-
-/**
- * Iterate over all real characters in the UTF-8 string.
- *
- * The function must have the following signature:
- *	void f(char ch)
- *
- * @param str the UTF-8 string
- * @throw std::invalid_argument on invalid sequence
- */
-template <typename Func>
-void forEach(const std::string &str, Func function)
-{
-	for (size_t i = 0; i < str.size(); ) {
-		char32_t point = 0;
-		int size = nbytesUtf8(str[i]);
-
-		if (size < 0) {
-			throw std::invalid_argument("invalid sequence");
-		}
-
-		decode(point, str.data() + i);
-		function(point);
-
-		i += size;
-	}
-}
-
-/**
- * Convert a UTF-32 string to UTF-8 string.
- *
- * @param array the UTF-32 string
- * @return the UTF-8 string
- * @throw std::invalid_argument on invalid sequence
- */
-std::string toUtf8(const std::u32string &array);
-
-/**
- * Convert a UTF-8 string to UTF-32 string.
- *
- * @param str the UTF-8 string
- * @return the UTF-32 string
- * @throw std::invalid_argument on invalid sequence
- */
-std::u32string toUtf32(const std::string &str);
-
-/**
- * Check if the unicode character is space.
- *
- * @param c the character
- * @return true if space
- */
-bool isspace(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is digit.
- *
- * @param c the character
- * @return true if digit
- */
-bool isdigit(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is alpha category.
- *
- * @param c the character
- * @return true if alpha
- */
-bool isalpha(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is upper case.
- *
- * @param c the character
- * @return true if upper case
- */
-bool isupper(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is lower case.
- *
- * @param c the character
- * @return true if lower case
- */
-bool islower(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is title case.
- *
- * @param c the character
- * @return true if title case
- */
-bool istitle(char32_t c) noexcept;
-
-/**
- * Convert to upper case.
- *
- * @param c the character
- * @return the upper case character
- */
-char32_t toupper(char32_t c) noexcept;
-
-/**
- * Convert to lower case.
- *
- * @param c the character
- * @return the lower case character
- */
-char32_t tolower(char32_t c) noexcept;
-
-/**
- * Convert to title case.
- *
- * @param c the character
- * @return the title case character
- */
-char32_t totitle(char32_t c) noexcept;
-
-/**
- * Convert the UTF-32 string to upper case.
- *
- * @param str the str
- * @return the upper case string
- */
-inline std::u32string toupper(std::u32string str)
-{
-	for (size_t i = 0; i < str.size(); ++i) {
-		str[i] = toupper(str[i]);
-	}
-
-	return str;
-}
-
-/**
- * Convert the UTF-8 string to upper case.
- *
- * @param str the str
- * @return the upper case string
- * @warning very slow at the moment
- */
-inline std::string toupper(const std::string &str)
-{
-	return toUtf8(toupper(toUtf32(str)));
-}
-
-/**
- * Convert the UTF-32 string to lower case.
- *
- * @param str the str
- * @return the lower case string
- */
-inline std::u32string tolower(std::u32string str)
-{
-	for (size_t i = 0; i < str.size(); ++i) {
-		str[i] = tolower(str[i]);
-	}
-
-	return str;
-}
-
-/**
- * Convert the UTF-8 string to lower case.
- *
- * @param str the str
- * @return the lower case string
- * @warning very slow at the moment
- */
-inline std::string tolower(const std::string &str)
-{
-	return toUtf8(tolower(toUtf32(str)));
-}
-
-} // !unicode
-
-} // !irccd
-
-#endif // !_UTF8_H_
--- a/common/util.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,357 +0,0 @@
-/*
- * util.cpp -- some utilities
- *
- * 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 <cctype>
-#include <cstdlib>
-#include <ctime>
-#include <iomanip>
-#include <sstream>
-#include <stdexcept>
-
-#include <irccd-config.h>
-
-#include "util.h"
-#include "unicode.h"
-
-using namespace std::string_literals;
-
-namespace irccd {
-
-namespace util {
-
-namespace {
-
-const std::unordered_map<std::string, int> colorTable{
-	{ "white",	0	},
-	{ "black",	1	},
-	{ "blue",	2	},
-	{ "green",	3	},
-	{ "red",	4	},
-	{ "brown",	5	},
-	{ "purple",	6	},
-	{ "orange",	7	},
-	{ "yellow",	8	},
-	{ "lightgreen",	9	},
-	{ "cyan",	10	},
-	{ "lightcyan",	11	},
-	{ "lightblue",	12	},
-	{ "pink",	13	},
-	{ "grey",	14	},
-	{ "lightgrey",	15	}
-};
-
-const std::unordered_map<std::string, char> attributesTable{
-	{ "bold",	'\x02'	},
-	{ "italic",	'\x09'	},
-	{ "strike",	'\x13'	},
-	{ "reset",	'\x0f'	},
-	{ "underline",	'\x15'	},
-	{ "underline2",	'\x1f'	},
-	{ "reverse",	'\x16'	}
-};
-
-std::string substituteDate(const std::string &text, const Substitution &params)
-{
-	std::ostringstream oss;
-
-#if defined(HAVE_STD_PUT_TIME)
-	oss << std::put_time(std::localtime(&params.time), text.c_str());
-#else
-	/*
-	 * Quick and dirty hack because GCC does not have this function.
-	 */
-	char buffer[4096];
-
-	std::strftime(buffer, sizeof (buffer) - 1, text.c_str(), std::localtime(&params.time));
-
-	oss << buffer;
-#endif
-
-	return oss.str();
-}
-
-std::string substituteKeywords(const std::string &content, const Substitution &params)
-{
-	auto value = params.keywords.find(content);
-
-	if (value != params.keywords.end())
-		return value->second;
-
-	return "";
-}
-
-std::string substituteEnv(const std::string &content)
-{
-	auto value = std::getenv(content.c_str());
-
-	if (value != nullptr)
-		return value;
-
-	return "";
-}
-
-std::string substituteAttributes(const std::string &content)
-{
-	std::stringstream oss;
-	std::vector<std::string> list = split(content, ",");
-
-	/* @{} means reset */
-	if (list.empty()) {
-		oss << attributesTable.at("reset");
-	} else {
-		/* Remove useless spaces */
-		for (auto &a : list)
-			a = strip(a);
-
-		/*
-		 * 0: foreground
-		 * 1: background
-		 * 2-n: attributes
-		 */
-		auto foreground = list[0];
-		if (!foreground.empty() || list.size() >= 2) {
-			/* Color sequence */
-			oss << '\x03';
-
-			/* Foreground */
-			auto it = colorTable.find(foreground);
-			if (it != colorTable.end())
-				oss << it->second;
-
-			/* Background */
-			if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end())
-				oss << "," << it->second;
-
-			/* Attributes */
-			for (std::size_t i = 2; i < list.size(); ++i) {
-				auto attribute = attributesTable.find(list[i]);
-
-				if (attribute != attributesTable.end())
-					oss << attribute->second;
-			}
-		}
-	}
-
-	return oss.str();
-}
-
-std::string substitute(std::string::const_iterator &it, std::string::const_iterator &end, char token, const Substitution &params)
-{
-	std::string content, value;
-
-	if (it == end)
-		return "";
-
-	while (it != end && *it != '}')
-		content += *it++;
-
-	if (*it != '}' || it == end)
-		throw std::invalid_argument("unclosed "s + token + " construct"s);
-
-	it++;
-
-	switch (token) {
-	case '#':
-		value = substituteKeywords(content, params);
-		break;
-	case '$':
-		value = substituteEnv(content);
-		break;
-	case '@':
-		value = substituteAttributes(content);
-		break;
-	default:
-		throw std::invalid_argument("unknown "s + token + " construct");
-	}
-
-	return value;
-}
-
-} // !namespace
-
-std::string format(std::string text, const Substitution &params)
-{
-	std::ostringstream oss;
-
-	/*
-	 * Change the date format before anything else to avoid interpolation with keywords and
-	 * user input.
-	 */
-	text = substituteDate(text, params);
-
-	std::string::const_iterator it = text.begin();
-	std::string::const_iterator end = text.end();
-
-	while (it != end) {
-		auto token = *it;
-
-		if (token == '#' || token == '@' || token == '$') {
-			++ it;
-
-			if (it == end) {
-				oss << token;
-				continue;
-			}
-
-			if (*it == '{') {
-				/* Do we have a variable? */
-				oss << substitute(++it, end, token, params);
-			} else if (*it == token) {
-				/* Need one for sure */
-				oss << token;
-
-				/* Do we have a double token followed by a { for escaping? */
-				if (++it == end)
-					continue;
-
-				if (*it != '{')
-					oss << token;
-			} else {
-				oss << *it++;
-			}
-		} else {
-			oss << *it++;
-		}
-	}
-
-	return oss.str();
-}
-
-std::string strip(std::string str)
-{
-	auto test = [] (char c) { return !std::isspace(c); };
-
-	str.erase(str.begin(), std::find_if(str.begin(), str.end(), test));
-	str.erase(std::find_if(str.rbegin(), str.rend(), test).base(), str.end());
-
-	return str;
-}
-
-std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max)
-{
-	std::vector<std::string> result;
-	size_t next = -1, current;
-	int count = 1;
-	bool finished = false;
-
-	if (list.empty())
-		return result;
-
-	do {
-		std::string val;
-
-		current = next + 1;
-		next = list.find_first_of(delimiters, current);
-
-		// split max, get until the end
-		if (max >= 0 && count++ >= max) {
-			val = list.substr(current, std::string::npos);
-			finished = true;
-		} else {
-			val = list.substr(current, next - current);
-			finished = next == std::string::npos;
-		}
-
-		result.push_back(val);
-	} while (!finished);
-
-	return result;
-}
-
-MessagePair parseMessage(std::string message, const std::string &cc, const std::string &name)
-{
-	std::string result = message;
-	bool iscommand = false;
-
-	// handle special commands "!<plugin> command"
-	if (cc.length() > 0) {
-		auto pos = result.find_first_of(" \t");
-		auto fullcommand = cc + name;
-
-		/*
-		 * If the message that comes is "!foo" without spaces we
-		 * compare the command char + the plugin name. If there
-		 * is a space, we check until we find a space, if not
-		 * typing "!foo123123" will trigger foo plugin.
-		 */
-		if (pos == std::string::npos)
-			iscommand = result == fullcommand;
-		else
-			iscommand = result.length() >= fullcommand.length() && result.compare(0, pos, fullcommand) == 0;
-
-		if (iscommand) {
-			/*
-			 * If no space is found we just set the message to "" otherwise
-			 * the plugin name will be passed through onCommand
-			 */
-			if (pos == std::string::npos)
-				result = "";
-			else
-				result = message.substr(pos + 1);
-		}
-	}
-
-	return MessagePair(result, ((iscommand) ? MessageType::Command : MessageType::Message));
-}
-
-bool isBoolean(std::string value) noexcept
-{
-	return (value = unicode::toupper(value)) == "1" || value == "YES" || value == "TRUE" || value == "ON";
-}
-
-bool isInt(const std::string &str, int base) noexcept
-{
-	if (str.empty())
-		return false;
-	
-	char *ptr;
-	
-	std::strtol(str.c_str(), &ptr, base);
-	
-	return *ptr == 0;
-}
-
-bool isReal(const std::string &str) noexcept
-{
-	if (str.empty())
-		return false;
-	
-	char *ptr;
-	
-	std::strtod(str.c_str(), &ptr);
-	
-	return *ptr == 0;
-}
-
-std::string nextNetwork(std::string &input)
-{
-	std::string result;
-	std::string::size_type pos = input.find("\r\n\r\n");
-
-	if ((pos = input.find("\r\n\r\n")) != std::string::npos) {
-		result = input.substr(0, pos);
-		input.erase(input.begin(), input.begin() + pos + 4);
-	}
-
-	return result;
-}
-
-} // util
-
-} // !irccd
--- a/common/util.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-/*
- * util.h -- some utilities
- *
- * 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_UTIL_H_
-#define _IRCCD_UTIL_H_
-
-#include <ctime>
-#include <initializer_list>
-#include <regex>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-namespace irccd {
-
-namespace util {
-
-/**
- * @enum MessageType
- * @brief Describe which type of message has been received
- *
- * On channels and queries, you may have a special command or a standard message depending on the
- * beginning of the message.
- *
- * Example: `!reminder help' may invoke the command event if a plugin reminder exists.
- */
-enum class MessageType {
-	Command,		//!< special command
-	Message			//!< standard message
-};
-
-/**
- * @brief Combine the type of message and its content.
- */
-using MessagePair = std::pair<std::string, MessageType>;
-
-/**
- * @class Substitution
- * @brief Used for format() function.
- */
-class Substitution {
-public:
-	/**
-	 * Fill that field if you want a date.
-	 */
-	std::time_t time{std::time(nullptr)};
-
-	/**
-	 * Fill that map if you want to replace keywords.
-	 */
-	std::unordered_map<std::string, std::string> keywords;
-};
-
-/**
- * Format a string and update all templates.
- *
- * Syntax
- * ======
- *
- * The syntax is ?{} where ? is replaced by one of the token defined below. Braces are mandatory and cannot be ommited.
- * To write a literal template construct, prepend the token twice.
- *
- * Availables templates
- * ====================
- *
- * The following templates are available:
- *
- * - #{name}, name will be substituted from the keywords in params
- * - ${name}, name will be substituted from the environment variable
- * - @{attributes}, the attributes will be substituted to IRC colors (see below)
- * - %, any format accepted by strftime(3)
- *
- * Attributes
- * ==========
- *
- * The attribute format is composed of three parts, foreground, background and modifiers, each separated by a comma.
- *
- * **Note:** you cannot omit parameters, to specify the background, you must specify the foreground.
- *
- * Examples
- * ========
- *
- * Valid constructs
- * ----------------
- *
- * - `#{target}, welcome`: if target is set to "irccd", becomes "irccd, welcome",
- * - `@{red}#{target}`: if target is specified, it is written in red,
- *
- * Invalid or literals constructs
- * ------------------------------
- *
- * - `##{target}`: will output "#{target}",
- * - `##`: will output "##",
- * - `#target`: will output "#target",
- * - `#{target`: will throw std::invalid_argument.
- *
- * Colors & attributes
- * -------------------
- *
- * - `@{red,blue}`: will write text red on blue background,
- * - `@{default,yellow}`: will write default color text on yellow background
- * - `@{white,black,bold,underline}`: will write white text on black in both bold and underline
- */
-std::string format(std::string text, const Substitution &params = {});
-
-/**
- * Remove leading and trailing spaces.
- *
- * @param str the string
- * @return the removed white spaces
- */
-std::string strip(std::string str);
-
-/**
- * Split a string by delimiters.
- *
- * @param list the string to split
- * @param delimiter a list of delimiters
- * @param max max number of split
- * @return a list of string splitted
- */
-std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max = -1);
-
-/**
- * Join values by a separator and return a string.
- *
- * @param first the first iterator
- * @param last the last iterator
- * @param delim the optional delimiter
- */
-template <typename InputIt, typename DelimType = char>
-std::string join(InputIt first, InputIt last, DelimType delim = ':')
-{
-	std::ostringstream oss;
-
-	if (first != last) {
-		oss << *first;
-
-		while (++first != last)
-			oss << delim << *first;
-	}
-
-	return oss.str();
-}
-
-/**
- * Convenient overload.
- *
- * @param list the initializer list
- * @param delim the delimiter
- * @return the string
- */
-template <typename T, typename DelimType = char>
-inline std::string join(std::initializer_list<T> list, DelimType delim = ':')
-{
-	return join(list.begin(), list.end(), delim);
-}
-
-/**
- * Parse IRC message and determine if it's a command or a simple message.
- *
- * @param message the message line
- * @param commandChar the command char (e.g '!')
- * @param plugin the plugin name
- * @return the pair
- */
-MessagePair parseMessage(std::string message, const std::string &commandChar, const std::string &plugin);
-
-/**
- * Server and identities must have strict names. This function can
- * be used to ensure that they are valid.
- *
- * @param name the identifier name
- * @return true if is valid
- */
-inline bool isIdentifierValid(const std::string &name)
-{
-	return std::regex_match(name, std::regex("[A-Za-z0-9-_]+"));
-}
-
-/**
- * Check if the value is a boolean, 1, yes and true are accepted.
- *
- * @param value the value
- * @return true if is boolean
- * @note this function is case-insensitive
- */
-bool isBoolean(std::string value) noexcept;
-
-/**
- * Check if the string is an integer.
- *
- * @param value the input
- * @param base the optional base
- * @return true if integer
- */
-bool isInt(const std::string &value, int base = 10) noexcept;
-
-/**
- * Check if the string is real.
- *
- * @param value the value
- * @return true if real
- */
-bool isReal(const std::string &value) noexcept;
-
-/**
- * Check if the string is a number.
- *
- * @param value the value
- * @return true if it is a number
- */
-inline bool isNumber(const std::string &value) noexcept
-{
-	return isInt(value) || isReal(value);
-}
-
-/**
- * Parse a network message from an input buffer and remove it from it.
- *
- * @param input the buffer, will be updated
- * @return the message or empty string if there is nothing
- */
-std::string nextNetwork(std::string &input);
-
-} // !util
-
-} // !irccd
-
-#endif // !_IRCCD_UTIL_H_
--- a/common/xdg.cpp	Mon Mar 14 11:24:25 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/common/xdg.h	Mon Mar 14 11:24:25 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/extern/duktape/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/extern/duktape/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -19,6 +19,7 @@
 project(duktape)
 
 irccd_define_library(
+	LOCAL
 	TARGET extern-duktape
 	SOURCES
 		duk_config.h
--- a/extern/jansson/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/extern/jansson/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -265,6 +265,7 @@
 )
 
 irccd_define_library(
+	LOCAL
 	TARGET extern-jansson
 	SOURCES
 		${JANSSON_SRC}
@@ -273,4 +274,4 @@
 	PUBLIC_INCLUDES
 		"${jansson_SOURCE_DIR}/include"
 		"${jansson_BINARY_DIR}/include"
-)
\ No newline at end of file
+)
--- a/extern/libircclient/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/extern/libircclient/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -47,6 +47,7 @@
 endif ()
 
 irccd_define_library(
+	LOCAL
 	TARGET extern-ircclient
 	SOURCES src/libircclient.c
 	FLAGS ${FLAGS}
--- a/irccd/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/irccd/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -18,120 +18,12 @@
 
 project(irccd)
 
-set(
-	SOURCES
-	command-plugin-info.cpp
-	command-plugin-info.h
-	command-plugin-list.cpp
-	command-plugin-list.h
-	command-plugin-load.cpp
-	command-plugin-load.h
-	command-plugin-reload.cpp
-	command-plugin-reload.h
-	command-plugin-unload.cpp
-	command-plugin-unload.h
-	command-server-cmode.cpp
-	command-server-cmode.h
-	command-server-cnotice.cpp
-	command-server-cnotice.h
-	command-server-connect.cpp
-	command-server-connect.h
-	command-server-disconnect.cpp
-	command-server-disconnect.h
-	command-server-info.cpp
-	command-server-info.h
-	command-server-invite.cpp
-	command-server-invite.h
-	command-server-join.cpp
-	command-server-join.h
-	command-server-kick.cpp
-	command-server-kick.h
-	command-server-list.cpp
-	command-server-list.h
-	command-server-me.cpp
-	command-server-me.h
-	command-server-message.cpp
-	command-server-message.h
-	command-server-mode.cpp
-	command-server-mode.h
-	command-server-nick.cpp
-	command-server-nick.h
-	command-server-notice.cpp
-	command-server-notice.h
-	command-server-part.cpp
-	command-server-part.h
-	command-server-reconnect.cpp
-	command-server-reconnect.h
-	command-server-topic.cpp
-	command-server-topic.h
-	config.cpp
-	config.h
-	irccd.cpp
-	irccd.h
-	main.cpp
-	rule.cpp
-	rule.h
-	server.cpp
-	server.h
-	server-state.cpp
-	server-state.h
-	transport-command.h
-	transport-server.cpp
-	transport-server.h
-	transport-client.cpp
-	transport-client.h
-)
-
-if (WITH_JS)
-	list(
-		APPEND
-		SOURCES
-		js.cpp
-		js.h
-		js-directory.cpp
-		js-directory.h
-		js-elapsed-timer.cpp
-		js-elapsed-timer.h
-		js-file.cpp
-		js-file.h
-		js-irccd.cpp
-		js-irccd.h
-		js-logger.cpp
-		js-logger.h
-		js-plugin.cpp
-		js-plugin.h
-		js-server.cpp
-		js-server.h
-		js-system.cpp
-		js-system.h
-		js-timer.cpp
-		js-timer.h
-		js-unicode.cpp
-		js-unicode.h
-		js-util.cpp
-		js-util.h
-		plugin.cpp
-		plugin.h
-		timer.cpp
-		timer.h
-	)
-
-	list(APPEND LIBRARIES extern-duktape)
-endif ()
-
-if (IRCCD_SYSTEM_MAC)
-	list(APPEND LIBRARIES resolv)
-endif ()
-
 irccd_define_executable(
 	TARGET irccd
 	INSTALL
-	SOURCES ${SOURCES}
+	SOURCES CMakeLists.txt main.cpp
 	INCLUDES ${irccd_SOURCE_DIR}
-	LIBRARIES
-		${LIBRARIES}
-		extern-ircclient
-		common
+	LIBRARIES libirccd
 )
 
 if (IRCCD_SYSTEM_MAC)
@@ -139,5 +31,5 @@
 endif ()
 
 if (UNIX)
-	set_target_properties(irccd PROPERTIES LINK_FLAGS "-pthread")
+	set_target_properties(irccd PROPERTIES LINK_FLAGS -pthread)
 endif ()
--- a/irccd/command-plugin-info.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * command-plugin-info.cpp -- implementation of plugin-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 <irccd-config.h>
-
-#include "command-plugin-info.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginInfo::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-#if defined(WITH_JS)
-	std::shared_ptr<Plugin> plugin = irccd.requirePlugin(object.at("plugin").toString());
-	json::Value result = json::object({
-		{ "response",	"plugin-info"		},
-		{ "author",	plugin->info().author	},
-		{ "license",	plugin->info().license	},
-		{ "summary",	plugin->info().summary	},
-		{ "version",	plugin->info().version	}
-	});
-
-	tc.send(result.toJson(0));
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
-
--- a/irccd/command-plugin-info.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-plugin-info.h -- implementation of plugin-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_PLUGIN_INFO_H_
-#define _IRCCD_COMMAND_PLUGIN_INFO_H_
-
-/**
- * @file command-plugin-info.h
- * @brief Implementation of plugin-info transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginInfo
- * @brief Implementation of plugin-load transport command.
- */
-class PluginInfo : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_INFO_H_
--- a/irccd/command-plugin-list.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +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 "command-plugin-list.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginList::exec(Irccd &irccd, TransportClient &tc, const json::Value &) const
-{
-#if defined(WITH_JS)
-	std::ostringstream oss;
-
-	oss << "{"
-	    <<   "\"response\":\"plugin-list\","
-	    <<   "\"status\":\"ok\","
-	    <<   "\"list\":[";
-
-	auto it = irccd.plugins().begin();
-	auto end = irccd.plugins().end();
-
-	while (it != end) {
-		oss << "\"" << it->first << "\"";
-
-		if (++it != end)
-			oss << ",";
-	}
-
-	oss << "]}";
-
-	tc.send(oss.str());
-#else
-	(void)irccd;
-	(void)tc;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-}
--- a/irccd/command-plugin-list.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginList
- * @brief Implementation of plugin-list transport command.
- */
-class PluginList : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_LIST_H_
--- a/irccd/command-plugin-load.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +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 "command-plugin-load.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginLoad::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-#if defined(WITH_JS)
-	std::string name = object.at("plugin").toString();
-
-	irccd.loadPlugin(name, name, true);
-	tc.ok("plugin-load");
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-plugin-load.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +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 command-plugin-load.h
- * @brief Implementation of plugin-load transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginLoad
- * @brief Implementation of plugin-load transport command.
- */
-class PluginLoad : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_LOAD_H_
--- a/irccd/command-plugin-reload.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +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 "command-plugin-reload.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginReload::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-#if defined(WITH_JS)
-	std::string name = object.at("plugin").toString();
-
-	irccd.requirePlugin(name)->onReload();
-	tc.ok("plugin-reload");
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-plugin-reload.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginReload
- * @brief Implementation of plugin-reload transport command.
- */
-class PluginReload : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_RELOAD_H_
--- a/irccd/command-plugin-unload.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * command-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 "command-plugin-unload.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginUnload::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-#if defined(WITH_JS)
-	std::string name = object.at("plugin").toString();
-
-	irccd.unloadPlugin(name);
-	tc.ok("plugin-unload");
-#else
-	(void)irccd;
-	(void)tc;
-	(void)object;
-
-	throw std::runtime_error("JavaScript disabled");
-#endif
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-plugin-unload.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-plugin-unload.h
- * @brief Implementation of plugin-unload transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginUnload
- * @brief Implementation of plugin-unload transport command.
- */
-class PluginUnload : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_PLUGIN_UNLOAD_H_
--- a/irccd/command-server-cmode.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * command-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 "command-server-cmode.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerChannelMode::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->cmode(
-		object.at("channel").toString(),
-		object.at("mode").toString()
-	);
-
-	tc.ok("server-cmode");
-}
-
-} // !command
-
-} // !irccd
-
--- a/irccd/command-server-cmode.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-cmode.h
- * @brief Implementation of server-cmode transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerChannelMode
- * @brief Implementation of server-cmode transport command.
- */
-class ServerChannelMode : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
--- a/irccd/command-server-cnotice.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * command-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 "command-server-cnotice.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerChannelNotice::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->cnotice(
-		object.at("channel").toString(),
-		object.at("message").toString()
-	);
-
-	tc.ok("server-cnotice");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-cnotice.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * command-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 command-server-cnotice.h
- * @brief Implementation of server-cnotice transport command.
- */
-
-#include "transport-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 TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CNOTICE_H_
--- a/irccd/command-server-connect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +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 <util.h>
-
-#include "command-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
-
-void ServerConnect::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	auto server = std::make_shared<Server>(readInfo(object), readIdentity(object), readSettings(object));
-
-	if (irccd.hasServer(server->info().name))
-		throw std::invalid_argument("server '" + server->info().name + "' already exists");
-
-	irccd.addServer(std::move(server));
-	tc.ok("server-connect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-connect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-connect.h
- * @brief Implementation of server-connect transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerConnect
- * @brief Implementation of server-connect transport command.
- */
-class ServerConnect : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_CONNECT_H_
--- a/irccd/command-server-disconnect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +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 "command-server-disconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerDisconnect::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	auto it = object.find("server");
-
-	if (it == object.end())
-		irccd.clearServers();
-	else
-		irccd.removeServer(it->toString());
-
-	tc.ok("server-disconnect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-disconnect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-disconnect.h
- * @brief Implementation of server-disconnect transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerDisconnect
- * @brief Implementation of server-disconnect transport command.
- */
-class ServerDisconnect : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_DISCONNECT_H_
--- a/irccd/command-server-info.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +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 "command-server-info.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerInfo::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	std::shared_ptr<Server> server = irccd.requireServer(object.at("server").toString());
-	json::Value result = json::object({
-		{ "response",	"server-info"			},
-		{ "name",	server->info().name		},
-		{ "host",	server->info().host		},
-		{ "port",	server->info().port		},
-		{ "nickname",	server->identity().nickname	},
-		{ "username",	server->identity().username	},
-		{ "realname",	server->identity().realname	}
-	});
-
-	if (server->info().flags & irccd::ServerInfo::Ipv6)
-		result.insert("ipv6", true);
-	if (server->info().flags & irccd::ServerInfo::Ssl)
-		result.insert("ssl", true);
-	if (server->info().flags & irccd::ServerInfo::SslVerify)
-		result.insert("sslVerify", true);
-
-	json::Value channels = json::array({});
-
-	for (const auto &c : server->settings().channels)
-		channels.append(c.name);
-
-	result.insert("channels", std::move(channels));
-	tc.send(result.toJson(0));
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-info.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInfo
- * @brief Implementation of server-info transport command.
- */
-class ServerInfo : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_INFO_H_
--- a/irccd/command-server-invite.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +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 "command-server-invite.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerInvite::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->invite(
-		object.at("target").toString(),
-		object.at("channel").toString()
-	);
-
-	tc.ok("server-invite");
-}
-
-} // !command
-
-} // !irccd
-
--- a/irccd/command-server-invite.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-invite.h
- * @brief Implementation of server-invite transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInvite
- * @brief Implementation of server-invite transport command.
- */
-class ServerInvite : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_INVITE_H_
--- a/irccd/command-server-join.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * command-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 "command-server-join.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerJoin::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->join(
-		object.at("channel").toString(),
-		object.valueOr("password", json::Type::String, "").toString()
-	);
-
-	tc.ok("server-join");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-join.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-join.h
- * @brief Implementation of server-join transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerJoin
- * @brief Implementation of server-join transport command.
- */
-class ServerJoin : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_JOIN_H_
--- a/irccd/command-server-kick.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +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 "command-server-kick.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerKick::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->kick(
-		object.at("target").toString(),
-		object.at("channel").toString(),
-		object.valueOr("reason", json::Type::String, "").toString()
-	);
-
-	tc.ok("server-kick");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-kick.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-kick.h
- * @brief Implementation of server-kick transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerKick
- * @brief Implementation of server-kick transport command.
- */
-class ServerKick : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_KICK_H_
--- a/irccd/command-server-list.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * command-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 "command-server-list.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerList::exec(Irccd &irccd, TransportClient &tc, const json::Value &) const
-{
-	std::ostringstream oss;
-
-	oss << "{"
-	    <<   "\"response\":\"server-list\","
-	    <<   "\"status\":\"ok\","
-	    <<   "\"list\":[";
-
-	auto it = irccd.servers().begin();
-	auto end = irccd.servers().end();
-
-	while (it != end) {
-		oss << "\"" << it->second->info().name << "\"";
-
-		if (++it != end)
-			oss << ",";
-	}
-
-	oss << "]}";
-
-	tc.send(oss.str());
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-list.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerList
- * @brief Implementation of server-list transport command.
- */
-class ServerList : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_LIST_H_
--- a/irccd/command-server-me.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * command-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 "command-server-me.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMe::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->me(
-		object.at("target").toString(),
-		object.at("message").toString()
-	);
-
-	tc.ok("server-me");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-me.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMe
- * @brief Implementation of server-me transport command.
- */
-class ServerMe : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_ME_H_
--- a/irccd/command-server-message.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * command-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 "command-server-message.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMessage::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->message(
-		object.at("target").toString(),
-		object.at("message").toString()
-	);
-
-	tc.ok("server-message");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-message.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMessage
- * @brief Implementation of server-message transport command.
- */
-class ServerMessage : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_MESSAGE_H_
--- a/irccd/command-server-mode.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +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 "command-server-mode.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMode::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->mode(object.at("mode").toString());
-	tc.ok("server-mode");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-mode.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-mode.h
- * @brief Implementation of server-mode transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMode
- * @brief Implementation of server-mode transport command.
- */
-class ServerMode : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_MODE_H_
--- a/irccd/command-server-nick.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * command-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 "command-server-nick.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerNick::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString());
-	tc.ok("server-nick");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-nick.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNick
- * @brief Implementation of server-nick transport command.
- */
-class ServerNick : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_NICK_H_
--- a/irccd/command-server-notice.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * command-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 "command-server-notice.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerNotice::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->notice(
-		object.at("target").toString(),
-		object.at("message").toString()
-	);
-
-	tc.ok("server-notice");
-}
-
-} // !command
-
-} // !irccd
-
--- a/irccd/command-server-notice.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-notice.h
- * @brief Implementation of server-notice transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNotice
- * @brief Implementation of server-notice transport command.
- */
-class ServerNotice : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_NOTICE_H_
--- a/irccd/command-server-part.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * command-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 "command-server-part.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerPart::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->part(
-		object.at("channel").toString(),
-		object.valueOr("reason", "").toString()
-	);
-
-	tc.ok("server-part");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-part.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-part.h
- * @brief Implementation of server-part transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerPart
- * @brief Implementation of server-part transport command.
- */
-class ServerPart : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_PART_H_
--- a/irccd/command-server-reconnect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +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 "command-server-reconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerReconnect::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	if (object.contains("server"))
-		irccd.requireServer(object.at("server").toString())->reconnect();
-	else
-		for (auto &pair : irccd.servers())
-			pair.second->reconnect();
-
-	tc.ok("server-reconnect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-reconnect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-reconnect.h
- * @brief Implementation of server-reconnect transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerReconnect
- * @brief Implementation of server-reconnect transport command.
- */
-class ServerReconnect : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_RECONNECT_H_
--- a/irccd/command-server-topic.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +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 "command-server-topic.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerTopic::exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const
-{
-	irccd.requireServer(object.at("server").toString())->topic(
-		object.at("channel").toString(),
-		object.at("topic").toString()
-	);
-
-	tc.ok("server-topic");
-}
-
-} // !command
-
-} // !irccd
--- a/irccd/command-server-topic.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-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 command-server-topic.h
- * @brief Implementation of server-topic transport command.
- */
-
-#include "transport-command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerTopic
- * @brief Implementation of server-topic transport command.
- */
-class ServerTopic : public TransportCommand {
-public:
-	/**
-	 * @copydoc TransportCommand::exec
-	 */
-	void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCD_COMMAND_SERVER_TOPIC_H_
--- a/irccd/config.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,571 +0,0 @@
-/*
- * config.cpp -- irccd configuration loader
- *
- * 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>
-
-#if defined(HAVE_GETPID)
-#  include <sys/types.h>
-#  include <unistd.h>
-#  include <cerrno>
-#  include <cstring>
-#  include <fstream>
-#endif
-
-#if defined(HAVE_DAEMON)
-#  include <cstdlib>
-#endif
-
-#include <ini.h>
-#include <logger.h>
-#include <path.h>
-#include <sockets.h>
-#include <system.h>
-#include <util.h>
-
-#include "config.h"
-#include "irccd.h"
-
-using namespace std;
-using namespace std::string_literals;
-
-namespace irccd {
-
-void Config::loadGeneral(const ini::Document &config) const
-{
-	ini::Document::const_iterator sc = config.find("general");
-	ini::Section::const_iterator it;
-
-#if defined(HAVE_GETPID)
-	if (sc != config.end()) {
-		it = sc->find("pidfile");
-
-		if (it != sc->end() && !it->value().empty()) {
-			std::string path = it->value();
-			std::ofstream out(path, std::ofstream::trunc);
-
-			if (!out) {
-				log::warning() << "irccd: could not open pidfile " << path << ": " << std::strerror(errno) << std::endl;
-			} else {
-				log::debug() << "irccd: pid written in " << path << std::endl;
-				out << getpid();
-			}
-		}
-	}
-#endif
-
-#if defined(HAVE_DAEMON)
-	/* CLI priority is higher */
-	bool daemonize = m_options.count("-f") == 0 && m_options.count("--foreground") == 0;
-
-	if (daemonize && sc != config.end()) {
-		it = sc->find("foreground");
-
-		if (it != sc->end())
-			daemonize = !util::isBoolean(it->value());
-	}
-
-	if (daemonize)
-		daemon(1, 0);
-#endif
-
-	if (sc != config.end()) {
-		try {
-#if defined(HAVE_SETGID)
-			if ((it = sc->find("gid")) != sc->end())
-				sys::setGid(it->value());
-#endif
-#if defined(HAVE_SETUID)
-			if ((it = sc->find("uid")) != sc->end())
-				sys::setUid(it->value());
-#endif
-		} catch (const std::exception &ex) {
-			log::warning() << "irccd: could not set " << it->key() << ": " << ex.what() << std::endl;
-		}
-	}
-}
-
-void Config::loadLogFile(const ini::Section &sc) const
-{
-	/*
-	 * TODO: improve that with CMake options.
-	 */
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	string normal = "log.txt";
-	string errors = "errors.txt";
-#else
-	string normal = "/var/log/irccd/log.txt";
-	string errors = "/var/log/irccd/errors.txt";
-#endif
-
-	ini::Section::const_iterator it;
-
-	if ((it = sc.find("path-logs")) != sc.end())
-		normal = it->value();
-	if ((it = sc.find("path-errors")) != sc.end())
-		errors = it->value();
-
-	log::setInterface(make_unique<log::File>(move(normal), move(errors)));
-}
-
-void Config::loadLogSyslog() const
-{
-#if defined(HAVE_SYSLOG)
-	log::setInterface(make_unique<log::Syslog>());
-#else
-	log::warning() << "irccd: syslog is not available on this platform" << endl;
-#endif // !HAVE_SYSLOG
-}
-
-void Config::loadLogs(const ini::Document &config) const
-{
-	ini::Document::const_iterator sc = config.find("logs");
-
-	if (sc == config.end())
-		return;
-
-	ini::Section::const_iterator it;
-
-	if ((it = sc->find("verbose")) != sc->end() && m_options.count("-v") == 0 && m_options.count("--verbose"))
-		log::setVerbose(util::isBoolean(it->value()));
-	if ((it = sc->find("type")) != sc->end()) {
-		/* Console is the default, no test case */
-		if (it->value() == "file")
-			loadLogFile(*sc);
-		else if (it->value() == "syslog")
-			loadLogSyslog();
-		else
-			log::warning() << "irccd: unknown log type: " << it->value() << std::endl;
-	}
-}
-
-void Config::loadPlugins(Irccd &irccd, const ini::Section &sc) const
-{
-#if defined(WITH_JS)
-	for (const ini::Option &option : sc) {
-		try {
-			if (option.value().empty())
-				irccd.loadPlugin(option.key(), option.key(), true);
-			else
-				irccd.loadPlugin(option.key(), option.value(), false);
-		} catch (const std::exception &ex) {
-			log::warning() << "plugin " << option.key() << ": " << ex.what() << std::endl;
-		}
-	}
-#else
-	(void)irccd;
-	(void)sc;
-#endif
-}
-
-void Config::loadPluginConfig(Irccd &irccd, const ini::Section &sc, string name) const
-{
-#if defined(WITH_JS)
-	PluginConfig config;
-
-	for (const ini::Option &option : sc)
-		config.emplace(option.key(), option.value());
-
-	irccd.addPluginConfig(std::move(name), std::move(config));
-#else
-	(void)irccd;
-	(void)sc;
-	(void)name;
-#endif
-}
-
-void Config::loadPlugins(Irccd &irccd, const ini::Document &config) const
-{
-#if defined(WITH_JS)
-	std::regex regex("^plugin\\.([A-Za-z0-9-_]+)$");
-	std::smatch match;
-
-	/*
-	 * Load plugin configurations before we load plugins since we use them
-	 * when we load the plugin itself.
-	 */
-	for (const ini::Section &section : config)
-		if (regex_match(section.key(), match, regex))
-			loadPluginConfig(irccd, section, match[1]);
-
-	ini::Document::const_iterator it = config.find("plugins");
-
-	if (it != config.end())
-		loadPlugins(irccd, *it);
-#else
-	(void)irccd;
-	(void)config;
-
-	log::warning() << "irccd: JavaScript disabled, ignoring plugins" << std::endl;
-#endif
-}
-
-void Config::loadServer(Irccd &irccd, const ini::Section &sc) const
-{
-	ServerInfo info;
-	ServerIdentity identity;
-	ServerSettings settings;
-
-	/* Name */
-	ini::Section::const_iterator it;
-
-	if ((it = sc.find("name")) == sc.end()) 
-		throw std::invalid_argument("server: missing name");
-	else if (!util::isIdentifierValid(it->value()))
-		throw std::invalid_argument("server " + it->value() + ": name is not valid");
-	else if (irccd.hasServer(it->value()))
-		throw std::invalid_argument("server " + it->value() + ": already exists");
-
-	info.name = it->value();
-
-	/* Host */
-	if ((it = sc.find("host")) == sc.end())
-		throw std::invalid_argument("server " + info.name + ": missing host");
-
-	info.host = it->value();
-
-	/* Optional identity */
-	if ((it = sc.find("identity")) != sc.end())
-		identity = irccd.findIdentity(it->value());
-
-	/* Optional port */
-	if ((it = sc.find("port")) != sc.end()) {
-		try {
-			info.port = std::stoi(it->value());
-		} catch (const std::exception &) {
-			throw std::invalid_argument("server " + info.name + ": invalid port number: " + it->value());
-		}
-	}
-
-	/* Optional password */
-	if ((it = sc.find("password")) != sc.end())
-		info.password = it->value();
-
-	/* Optional flags */
-	if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value()))
-		info.flags |= ServerInfo::Ipv6;
-	if ((it = sc.find("ssl")) != sc.end())
-		if (util::isBoolean(it->value()))
-#if defined(WITH_SSL)
-			info.flags |= ServerInfo::Ssl;
-#else
-			throw std::invalid_argument("server " + info.name + ": ssl is disabled");
-#endif
-
-	if ((it = sc.find("ssl-verify")) != sc.end())
-		if (util::isBoolean(it->value()))
-#if defined(WITH_SSL)
-			info.flags |= ServerInfo::SslVerify;
-#else
-			throw std::invalid_argument("server " + info.name + ": ssl is disabled");
-#endif
-
-	/* Options */
-	if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value()))
-		settings.flags |= ServerSettings::AutoRejoin;
-	if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value()))
-		settings.flags |= ServerSettings::JoinInvite;
-
-	/* Channels */
-	if ((it = sc.find("channels")) != sc.end()) {
-		for (const std::string &s : *it) {
-			ServerChannel channel;
-
-			if (auto pos = s.find(":") != std::string::npos) {
-				channel.name = s.substr(0, pos);
-				channel.password = s.substr(pos + 1);
-			} else {
-				channel.name = s;
-			}
-
-			settings.channels.push_back(std::move(channel));
-		}
-	}
-	if ((it = sc.find("command-char")) != sc.end())
-		settings.command = it->value();
-
-	/* Reconnect */
-	try {
-		if ((it = sc.find("reconnect-tries")) != sc.end())
-			settings.recotries = std::stoi(it->value());
-		if ((it = sc.find("reconnect-timeout")) != sc.end())
-			settings.recotimeout = std::stoi(it->value());
-	} catch (const std::exception &) {
-		throw std::invalid_argument("server " + info.name + ": invalid number for " + it->key() + ": " + it->value());
-	}
-
-	irccd.addServer(std::make_shared<Server>(std::move(info), std::move(identity), std::move(settings)));
-}
-
-void Config::loadServers(Irccd &irccd, const ini::Document &config) const
-{
-	for (const ini::Section &section : config) {
-		if (section.key() == "server") {
-			try {
-				loadServer(irccd, section);
-			} catch (const exception &ex) {
-				log::warning() << ex.what() << endl;
-			}
-		}
-	}
-}
-
-void Config::loadIdentity(Irccd &irccd, const ini::Section &sc) const
-{
-	ServerIdentity identity;
-	ini::Section::const_iterator it;
-
-	if ((it = sc.find("name")) == sc.end()) {
-		throw invalid_argument("missing name");
-	} else if (!util::isIdentifierValid(it->value())) {
-		throw invalid_argument("identity name not valid");
-	}
-
-	identity.name = it->value();
-
-	/* Optional stuff */
-	if ((it = sc.find("username")) != sc.end())
-		identity.username = it->value();
-	if ((it = sc.find("realname")) != sc.end())
-		identity.realname = it->value();
-	if ((it = sc.find("nickname")) != sc.end())
-		identity.nickname = it->value();
-	if ((it = sc.find("ctcp-version")) != sc.end())
-		identity.ctcpversion = it->value();
-
-	log::debug() << "identity " << identity.name << ": ";
-	log::debug() << "nickname=" << identity.nickname << ", username=" << identity.username << ", ";
-	log::debug() << "realname=" << identity.realname << ", ctcp-version=" << identity.ctcpversion << endl;
-
-	irccd.addIdentity(move(identity));
-}
-
-void Config::loadIdentities(Irccd &irccd, const ini::Document &config) const
-{
-	for (const ini::Section &section : config) {
-		if (section.key() == "identity") {
-			try {
-				loadIdentity(irccd, section);
-			} catch (const exception &ex) {
-				log::warning() << "identity: " << ex.what() << endl;
-			}
-		}
-	}
-}
-
-void Config::loadRule(Irccd &irccd, const ini::Section &sc) const
-{
-	/* Simple converter from std::vector to std::unordered_set */
-	auto toSet = [] (const std::vector<std::string> &v) -> std::unordered_set<std::string> {
-		return std::unordered_set<std::string>(v.begin(), v.end());
-	};
-
-	RuleSet servers, channels, origins, plugins, events;
-	RuleAction action = RuleAction::Accept;
-
-	/* Get the sets */
-	ini::Section::const_iterator it;
-
-	if ((it = sc.find("servers")) != sc.end())
-		servers = toSet(*it);
-	if ((it = sc.find("channels")) != sc.end())
-		channels = toSet(*it);
-	if ((it = sc.find("origins")) != sc.end())
-		origins = toSet(*it);
-	if ((it = sc.find("plugins")) != sc.end())
-		plugins = toSet(*it);
-	if ((it = sc.find("channels")) != sc.end())
-		channels = toSet(*it);
-
-	/* Get the action */
-	if ((it = sc.find("action")) == sc.end())
-		throw std::invalid_argument("missing action parameter");
-	if (it->value() == "drop")
-		action = RuleAction::Drop;
-	else if (it->value() == "accept")
-		action = RuleAction::Accept;
-	else
-		throw std::invalid_argument("invalid action given: " + it->value());
-
-	irccd.addRule(Rule(move(servers), move(channels), move(origins), move(plugins), move(events), action));
-}
-
-void Config::loadRules(Irccd &irccd, const ini::Document &config) const
-{
-	for (const ini::Section &sc : config) {
-		if (sc.key() == "rule") {
-			try {
-				loadRule(irccd, sc);
-			} catch (const std::exception &ex) {
-				log::warning() << "rule: " << ex.what() << std::endl;
-			}
-		}
-	}
-}
-
-void Config::loadTransportIp(Irccd &irccd, const ini::Section &sc) const
-{
-	bool ipv6 = true;
-	bool ipv4 = true;
-
-	ini::Section::const_iterator it;
-
-	/* Port */
-	int port;
-
-	if ((it = sc.find("port")) == sc.end())
-		throw invalid_argument("missing port");
-
-	try {
-		port = stoi(it->value());
-	} catch (const std::exception &) {
-		throw std::invalid_argument("invalid port number: " + it->value());
-	}
-
-	/* Address*/
-	std::string address = "*";
-
-	if ((it = sc.find("address")) != sc.end())
-		address = it->value();
-
-	/* Domain */
-	if ((it = sc.find("domain")) != sc.end()) {
-		ipv6 = false;
-		ipv4 = false;
-
-		for (const string &v : *it) {
-			if (v == "ipv4")
-				ipv4 = true;
-			if (v == "ipv6")
-				ipv6 = true;
-		}
-	}
-
-	if (ipv6)
-		irccd.addTransport(std::make_shared<TransportServerIp>(AF_INET6, move(address), port, !ipv4));
-	else if (ipv4)
-		irccd.addTransport(std::make_shared<TransportServerIp>(AF_INET, move(address), port));
-	else
-		throw std::invalid_argument("domain must at least have ipv4 or ipv6");
-}
-
-void Config::loadTransportUnix(Irccd &irccd, const ini::Section &sc) const
-{
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-	/* Path */
-	ini::Section::const_iterator it = sc.find("path");
-
-	if (it == sc.end()) {
-		throw std::invalid_argument("missing path parameter");
-	} else {
-		string path = sc["path"].value();
-
-		irccd.addTransport(std::make_shared<TransportServerUnix>(move(path)));
-	}
-#else
-	(void)irccd;
-	(void)sc;
-
-	throw std::invalid_argument("local transport not supported on on this platform");
-#endif
-}
-
-void Config::loadTransports(Irccd &irccd, const ini::Document &config) const
-{
-	for (const ini::Section &sc : config) {
-		if (sc.key() == "transport") {
-			try {
-				ini::Section::const_iterator it = sc.find("type");
-
-				if (it == sc.end())
-					log::warning() << "transport: missing type parameter" << std::endl;
-				else if (it->value() == "ip")
-					loadTransportIp(irccd, sc);
-				else if (it->value() == "unix")
-					loadTransportUnix(irccd, sc);
-				else
-					log::warning() << "transport: invalid type given: " << std::endl;
-			} catch (const net::Error &error) {
-				log::warning() << "transport: " << error.function() << ": " << error.what() << std::endl;
-			} catch (const exception &ex) {
-				log::warning() << "transport: error: " << ex.what() << endl;
-			}
-		}
-	}
-}
-
-bool Config::openConfig(Irccd &irccd, const string &path) const
-{
-	try {
-		/*
-		 * Order matters, take care when you change this.
-		 */
-		ini::Document config(ini::File{path});
-
-		loadGeneral(config);
-		loadLogs(config);
-		loadIdentities(irccd, config);
-		loadServers(irccd, config);
-		loadRules(irccd, config);
-		loadPlugins(irccd, config);
-		loadTransports(irccd, config);
-	} catch (const ini::Error &ex) {
-		log::warning() << sys::programName() << ": " << path << ":" << ex.line() << ":" << ex.column() << ": " << ex.what() << std::endl;
-		return false;
-	} catch (const std::exception &ex) {
-		log::warning() << sys::programName() << ": " << path << ": " << ex.what() << std::endl;
-		return false;
-	}
-
-	return true;
-}
-
-Config::Config(parser::Result options) noexcept
-	: m_options(move(options))
-{
-}
-
-void Config::load(Irccd &irccd)
-{
-	auto it = m_options.find("-c");
-	auto found = false;
-
-	if (it != m_options.end())
-		found = openConfig(irccd, it->second);
-	else if ((it = m_options.find("--config")) != m_options.end())
-		found = openConfig(irccd, it->second);
-	else {
-		/* Search for a configuration file */
-		for (const string &path : path::list(path::PathConfig)) {
-			string fullpath = path + "irccd.conf";
-
-			log::info() << "irccd: trying " << fullpath << endl;
-
-			if (openConfig(irccd, fullpath)) {
-				found = true;
-				break;
-			}
-		}
-	}
-
-	if (!found) {
-		log::warning() << "irccd: no configuration file could be found, exiting" << endl;
-		exit(1);
-	}
-}
-
-} // !irccd
--- a/irccd/config.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * config.h -- irccd configuration loader
- *
- * 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_CONFIG_LOADER_H_
-#define _IRCCD_CONFIG_LOADER_H_
-
-/**
- * @file Config.h
- * @brief Read .ini configuration file for irccd
- */
-
-#include <options.h>
-
-namespace irccd {
-
-namespace ini {
-
-class Document;
-class Section;
-
-} // !ini
-
-class Irccd;
-
-/**
- * @class Config
- * @brief Read .ini configuration file for irccd
- */
-class Config {
-private:
-	parser::Result m_options;
-
-	void loadGeneral(const ini::Document &config) const;
-	void loadLogFile(const ini::Section &sc) const;
-	void loadLogSyslog() const;
-	void loadLogs(const ini::Document &config) const;
-	void loadPlugins(Irccd &irccd, const ini::Section &sc) const;
-	void loadPluginConfig(Irccd &irccd, const ini::Section &sc, std::string name) const;
-	void loadPlugins(Irccd &irccd, const ini::Document &config) const;
-	void loadServer(Irccd &irccd, const ini::Section &sc) const;
-	void loadServers(Irccd &irccd, const ini::Document &config) const;
-	void loadIdentity(Irccd &irccd, const ini::Section &sc) const;
-	void loadIdentities(Irccd &irccd, const ini::Document &config) const;
-	void loadRule(Irccd &irccd, const ini::Section &sc) const;
-	void loadRules(Irccd &irccd, const ini::Document &config) const;
-	void loadTransportIp(Irccd &irccd, const ini::Section &sc) const;
-	void loadTransportUnix(Irccd &irccd, const ini::Section &sc) const;
-	void loadTransports(Irccd &irccd, const ini::Document &config) const;
-	bool openConfig(Irccd &irccd, const std::string &path) const;
-
-public:
-	/**
-	 * Construct the configuration file loader. If path is empty, then the configuration file is searched through
-	 * the standard directories.
-	 *
-	 * @param options the option parsed at command line
-	 */
-	Config(parser::Result options) noexcept;
-
-	/**
-	 * Load the config into irccd.
-	 *
-	 * @param irccd the irccd instance
-	 */
-	void load(Irccd &irccd);
-};
-
-} // !irccd
-
-#endif // !_IRCCD_CONFIG_LOADER_H_
--- a/irccd/irccd.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1044 +0,0 @@
-/*
- * irccd.cpp -- main irccd class
- *
- * 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 <stdexcept>
-
-#include <filesystem.h>
-#include <logger.h>
-#include <path.h>
-#include <util.h>
-
-#include "irccd.h"
-
-using namespace std;
-using namespace std::placeholders;
-using namespace std::string_literals;
-
-namespace irccd {
-
-void Irccd::handleServerChannelMode(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string mode, std::string arg)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onChannelMode:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  mode: " << mode << "\n";
-	log::debug() << "  argument: " << arg << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onChannelMode"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "mode",	mode			},
-		{ "argument",	arg			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onChannelMode";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onChannelMode(std::move(server), std::move(origin), std::move(channel), std::move(mode), std::move(arg));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerChannelNotice(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onChannelNotice:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onChannelNotice"	},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "message",	message			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onChannelNotice";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onChannelNotice(std::move(server), std::move(origin), std::move(channel), std::move(message));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerConnect(std::weak_ptr<Server> ptr)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onConnect" << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onConnect"		},
-		{ "server",	server->info().name	}
-	});
-
-	postServerEvent({server->info().name, /* origin */ "", /* channel */ "", json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onConnect";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onConnect(std::move(server));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerInvite(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onInvite:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  target: " << target << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onInvite"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onInvite";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onInvite(std::move(server), std::move(origin), std::move(channel));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerJoin(std::weak_ptr<Server> ptr, std::string origin, std::string channel)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onJoin:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onJoin"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onJoin";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onJoin(std::move(server), std::move(origin), std::move(channel));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerKick(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target, std::string reason)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onKick:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  target: " << target << "\n";
-	log::debug() << "  reason: " << reason << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onKick"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "target",	target			},
-		{ "reason",	reason			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onKick";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onKick(std::move(server), std::move(origin), std::move(channel), std::move(target), std::move(reason));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerMessage(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onMessage:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onMessage"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "message",	message			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &plugin) -> std::string {
-			return util::parseMessage(message, server->settings().command, plugin.info().name).second == util::MessageType::Command ? "onCommand" : "onMessage";
-		}
-		, [=] (Plugin &plugin) {
-			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.info().name);
-
-			if (pack.second == util::MessageType::Command)
-				plugin.onCommand(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
-			else
-				plugin.onMessage(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerMe(std::weak_ptr<Server> ptr, std::string origin, std::string target, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onMe:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  target: " << target << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onMe"			},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "target",	target			},
-		{ "message",	message			}
-	});
-
-	postServerEvent({server->info().name, origin, target, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onMe";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onMe(std::move(server), std::move(origin), std::move(target), std::move(message));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerMode(std::weak_ptr<Server> ptr, std::string origin, std::string mode)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onMode\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  mode: " << mode << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onMode"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "mode",	mode			}
-	});
-
-	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onMode";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onMode(std::move(server), std::move(origin), std::move(mode));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerNames(std::weak_ptr<Server> ptr, std::string channel, std::set<std::string> nicknames)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onNames:\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  names: " << util::join(nicknames.begin(), nicknames.end(), ", ") << std::endl;
-
-	json::Value names(std::vector<json::Value>(nicknames.begin(), nicknames.end()));
-	json::Value json = json::object({
-		{ "event",	"onNames"		},
-		{ "server",	server->info().name	},
-		{ "channel",	channel			},
-		{ "names",	std::move(names)	}
-	});
-
-	postServerEvent({server->info().name, /* origin */ "", channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onNames";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onNames(std::move(server), std::move(channel), std::vector<std::string>(nicknames.begin(), nicknames.end()));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerNick(std::weak_ptr<Server> ptr, std::string origin, std::string nickname)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onNick:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  nickname: " << nickname << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onNick"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "nickname",	nickname		}
-	});
-
-	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onNick";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onNick(std::move(server), std::move(origin), std::move(nickname));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerNotice(std::weak_ptr<Server> ptr, std::string origin, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onNotice:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onNotice"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "message",	message			}
-	});
-
-	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onNotice";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onNotice(std::move(server), std::move(origin), std::move(message));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerPart(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string reason)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onPart:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  reason: " << reason << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onPart"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "reason",	reason			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onPart";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onPart(std::move(server), std::move(origin), std::move(channel), std::move(reason));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerQuery(std::weak_ptr<Server> ptr, std::string origin, std::string message)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onQuery:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  message: " << message << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onQuery"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "message",	message			}
-	});
-
-	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &plugin) -> std::string {
-			return util::parseMessage(message, server->settings().command, plugin.info().name).second == util::MessageType::Command ? "onQueryCommand" : "onQuery";
-		}
-		, [=] (Plugin &plugin) {
-			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.info().name);
-
-			if (pack.second == util::MessageType::Command)
-				plugin.onQueryCommand(std::move(server), std::move(origin), std::move(pack.first));
-			else
-				plugin.onQuery(std::move(server), std::move(origin), std::move(pack.first));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerTopic(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string topic)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onTopic:\n";
-	log::debug() << "  origin: " << origin << "\n";
-	log::debug() << "  channel: " << channel << "\n";
-	log::debug() << "  topic: " << topic << std::endl;
-
-	json::Value json = json::object({
-		{ "event",	"onTopic"		},
-		{ "server",	server->info().name	},
-		{ "origin",	origin			},
-		{ "channel",	channel			},
-		{ "topic",	topic			}
-	});
-
-	postServerEvent({server->info().name, origin, channel, json.toJson(0)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onTopic";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onTopic(std::move(server), std::move(origin), std::move(channel), std::move(topic));
-		}
-#endif
-	});
-}
-
-void Irccd::handleServerWhois(std::weak_ptr<Server> ptr, ServerWhois whois)
-{
-	std::shared_ptr<Server> server = ptr.lock();
-
-	if (!server)
-		return;
-
-	log::debug() << "server " << server->info().name << ": event onWhois\n";
-	log::debug() << "  nickname: " << whois.nick << "\n";
-	log::debug() << "  username: " << whois.user << "\n";
-	log::debug() << "  host: " << whois.host << "\n";
-	log::debug() << "  realname: " << whois.realname << "\n";
-	log::debug() << "  channels: " << util::join(whois.channels.begin(), whois.channels.end()) << std::endl;
-
-	json::Value object = json::object({
-		{ "server",	server->info().name	},
-		{ "nickname",	whois.nick		},
-		{ "username",	whois.user		},
-		{ "host",	whois.host		},
-		{ "realname",	whois.realname		}
-	});
-
-	postServerEvent({server->info().name, /* origin */ "", /* channel */ "", object.toJson(-1)
-#if defined(WITH_JS)
-		, [=] (Plugin &) -> std::string {
-			return "onWhois";
-		}
-		, [=] (Plugin &plugin) {
-			plugin.onWhois(std::move(server), std::move(whois));
-		}
-#endif
-	});
-}
-
-void Irccd::handleTransportCommand(std::weak_ptr<TransportClient> ptr, const json::Value &object)
-{
-	assert(object.isObject());
-
-	post([=] () {
-		/* 0. Be sure the object still exists */
-		auto tc = ptr.lock();
-
-		if (!tc)
-			return;
-
-		/* 1. Check if the Json object is valid */
-		auto name = object.find("command");
-		if (name == object.end() || name->typeOf() != json::Type::String) {
-			// TODO: send error
-			log::warning() << "invalid command object" << std::endl;
-			return;
-		}
-
-		/* 2. Search for a command */
-		auto it = m_transportCommands.find(name->toString());
-
-		if (it == m_transportCommands.end()) {
-			// TODO: send error again
-			log::warning() << "command does not exists" << std::endl;
-			return;
-		}
-
-		/* 3. Try to execute it */
-		try {
-			it->second->exec(*this, *tc, object);
-		} catch (const std::exception &ex) {
-			tc->error(it->first, ex.what());
-		}
-	});
-}
-
-void Irccd::handleTransportDie(std::weak_ptr<TransportClient> ptr)
-{
-	post([=] () {
-		log::info() << "transport: client disconnected" << std::endl;
-
-		auto tc = ptr.lock();
-
-		if (tc)
-			m_lookupTransportClients.erase(tc->handle());
-	});
-}
-
-void Irccd::processIpc(fd_set &input)
-{
-	if (FD_ISSET(m_socketServer.handle(), &input)) {
-		try {
-			(void)m_socketServer.recv(8);
-		} catch (const exception &) {
-			// TODO: think what we can do here
-		}
-	}
-}
-
-void Irccd::processTransportClients(fd_set &input, fd_set &output)
-{
-	for (auto &pair : m_lookupTransportClients)
-		pair.second->sync(input, output);
-}
-
-void Irccd::processTransportServers(fd_set &input)
-{
-	for (auto &pair : m_lookupTransportServers) {
-		if (!FD_ISSET(pair.second->handle(), &input))
-			continue;
-
-		log::debug() << "transport: new client connected" << endl;
-
-		std::shared_ptr<TransportClient> client = pair.second->accept();
-		std::weak_ptr<TransportClient> ptr(client);
-
-		/* Send some information */
-		json::Value object = json::object({
-			{ "program",	"irccd"			},
-			{ "major",	IRCCD_VERSION_MAJOR	},
-			{ "minor",	IRCCD_VERSION_MINOR	},
-			{ "patch",	IRCCD_VERSION_PATCH	}
-		});
-
-#if defined(WITH_JS)
-		object.insert("javascript", true);
-#endif
-#if defined(WITH_SSL)
-		object.insert("ssl", true);
-#endif
-
-		client->send(object.toJson(0));
-
-		/* Connect signals */
-		client->onCommand.connect(std::bind(&Irccd::handleTransportCommand, this, ptr, _1));
-		client->onDie.connect(std::bind(&Irccd::handleTransportDie, this, ptr));
-
-		/* Register it */
-		m_lookupTransportClients.emplace(client->handle(), move(client));
-	}
-}
-
-void Irccd::processServers(fd_set &input, fd_set &output)
-{
-	for (auto &pair : m_servers)
-		pair.second->sync(input, output);
-}
-
-void Irccd::process(fd_set &setinput, fd_set &setoutput)
-{
-	/* 1. May be IPC */
-	processIpc(setinput);
-
-	/* 2. Check for transport clients */
-	processTransportClients(setinput, setoutput);
-
-	/* 3. Check for transport servers */
-	processTransportServers(setinput);
-
-	/* 4. Check for servers */
-	processServers(setinput, setoutput);
-}
-
-void Irccd::postServerEvent(ServerEvent event) noexcept
-{
-#if defined(WITH_JS)
-	post([=] () {
-		for (auto &pair : m_plugins) {
-			auto name = event.name(*pair.second);
-			auto allowed = Rule::solve(m_rules, event.server, event.target, event.origin, pair.first, name);
-
-			if (!allowed) {
-				log::debug() << "rule: event skipped on match" << std::endl;
-				continue;
-			} else {
-				log::debug() << "rule: event allowed" << std::endl;
-			}
-
-			try {
-				event.exec(*pair.second);
-			} catch (const js::ErrorInfo &info) {
-				log::warning() << "plugin " << pair.second->info().name << ": error: " << info.what() << std::endl;
-
-				if (!info.fileName.empty())
-					log::warning() << "    " << info.fileName << ":" << info.lineNumber << std::endl;
-				if (!info.stack.empty())
-					log::warning() << "    " << info.stack << std::endl;
-			}
-		}
-	});
-#endif
-
-	/* Asynchronous send */
-	for (auto &pair : m_lookupTransportClients)
-		pair.second->send(event.json);
-}
-
-Irccd::Irccd()
-{
-	/* Bind a socket to any port */
-	m_socketServer.set(net::option::SockReuseAddress{true});
-	m_socketServer.bind(net::address::Ip{"*", 0});
-	m_socketServer.listen(1);
-
-	/* Do the socket pair */
-	m_socketClient.connect(net::address::Ip{"127.0.0.1", m_socketServer.address().port()});
-	m_socketServer = m_socketServer.accept(nullptr);
-	m_socketClient.set(net::option::SockBlockMode{false});
-}
-
-void Irccd::post(Event ev) noexcept
-{
-	std::lock_guard<mutex> lock(m_mutex);
-
-	m_events.push_back(move(ev));
-
-	/* Silently discard */
-	try {
-		m_socketClient.send(" ");
-	} catch (...) {
-	}
-}
-
-void Irccd::addServer(shared_ptr<Server> server) noexcept
-{
-	assert(m_servers.count(server->info().name) == 0);
-
-	std::weak_ptr<Server> ptr(server);
-
-	server->onChannelMode.connect(std::bind(&Irccd::handleServerChannelMode, this, ptr, _1, _2, _3, _4));
-	server->onChannelNotice.connect(std::bind(&Irccd::handleServerChannelNotice, this, ptr, _1, _2, _3));
-	server->onConnect.connect(std::bind(&Irccd::handleServerConnect, this, ptr));
-	server->onInvite.connect(std::bind(&Irccd::handleServerInvite, this, ptr, _1, _2, _3));
-	server->onJoin.connect(std::bind(&Irccd::handleServerJoin, this, ptr, _1, _2));
-	server->onKick.connect(std::bind(&Irccd::handleServerKick, this, ptr, _1, _2, _3, _4));
-	server->onMessage.connect(std::bind(&Irccd::handleServerMessage, this, ptr, _1, _2, _3));
-	server->onMe.connect(std::bind(&Irccd::handleServerMe, this, ptr, _1, _2, _3));
-	server->onMode.connect(std::bind(&Irccd::handleServerMode, this, ptr, _1, _2));
-	server->onNames.connect(std::bind(&Irccd::handleServerNames, this, ptr, _1, _2));
-	server->onNick.connect(std::bind(&Irccd::handleServerNick, this, ptr, _1, _2));
-	server->onNotice.connect(std::bind(&Irccd::handleServerNotice, this, ptr, _1, _2));
-	server->onPart.connect(std::bind(&Irccd::handleServerPart, this, ptr, _1, _2, _3));
-	server->onQuery.connect(std::bind(&Irccd::handleServerQuery, this, ptr, _1, _2));
-	server->onTopic.connect(std::bind(&Irccd::handleServerTopic, this, ptr, _1, _2, _3));
-	server->onWhois.connect(std::bind(&Irccd::handleServerWhois, this, ptr, _1));
-	server->onDie.connect([this, ptr] () {
-		post([=] () {
-			auto server = ptr.lock();
-
-			if (server) {
-				log::info() << "server " << server->info().name << ": removed" << std::endl;
-				m_servers.erase(server->info().name);
-			}
-		});
-	});
-
-	m_servers.emplace(server->info().name, move(server));
-}
-
-std::shared_ptr<Server> Irccd::getServer(const std::string &name) const noexcept
-{
-	auto it = m_servers.find(name);
-
-	if (it == m_servers.end())
-		return nullptr;
-
-	return it->second;
-}
-
-std::shared_ptr<Server> Irccd::requireServer(const std::string &name) const
-{
-	auto it = m_servers.find(name);
-
-	if (it == m_servers.end())
-		throw std::invalid_argument("server " + name + " not found");
-
-	return it->second;
-}
-
-void Irccd::removeServer(const std::string &name)
-{
-	auto it = m_servers.find(name);
-
-	if (it != m_servers.end()) {
-		it->second->disconnect();
-		m_servers.erase(it);
-	}
-}
-
-void Irccd::clearServers() noexcept
-{
-	for (auto &pair : m_servers)
-		pair.second->disconnect();
-
-	m_servers.clear();
-}
-
-void Irccd::addTransport(std::shared_ptr<TransportServer> ts)
-{
-	m_lookupTransportServers.emplace(ts->handle(), ts);
-}
-
-#if defined(WITH_JS)
-
-std::shared_ptr<Plugin> Irccd::getPlugin(const std::string &name) const noexcept
-{
-	auto it = m_plugins.find(name);
-
-	if (it == m_plugins.end())
-		return nullptr;
-
-	return it->second;
-}
-
-std::shared_ptr<Plugin> Irccd::requirePlugin(const std::string &name) const
-{
-	auto it = m_plugins.find(name);
-
-	if (it == m_plugins.end())
-		throw std::out_of_range(std::string("plugin ") + name + " not found");
-
-	return it->second;
-}
-
-void Irccd::addPlugin(std::shared_ptr<Plugin> plugin)
-{
-	std::weak_ptr<Plugin> ptr(plugin);
-
-	plugin->onTimerSignal.connect(std::bind(&Irccd::handleTimerSignal, this, ptr, _1));
-	plugin->onTimerEnd.connect(std::bind(&Irccd::handleTimerEnd, this, ptr, _1));
-
-	/* Store reference to irccd */
-	plugin->context().putGlobal("\xff""\xff""irccd", js::RawPointer<Irccd>{this});
-
-	/* Initial load now */
-	try {
-		plugin->onLoad();
-		m_plugins.insert({plugin->info().name, plugin});
-	} catch (const std::exception &ex) {
-		log::info() << "plugin " << plugin->info().name << ": " << ex.what() << std::endl;
-	}
-}
-
-void Irccd::loadPlugin(std::string name, const std::string &source, bool find)
-{
-	if (m_plugins.count(name) > 0)
-		throw std::invalid_argument("plugin already loaded");
-
-	std::vector<string> paths;
-	std::shared_ptr<Plugin> plugin;
-
-	if (find)
-		for (const std::string &dir : path::list(path::PathPlugins))
-			paths.push_back(dir + source + ".js");
-	else
-		paths.push_back(source);
-
-	/* Iterate over all paths */
-	log::info() << "plugin " << name << ": trying to load:" << std::endl;
-
-	for (const auto &path : paths) {
-		log::info() << "  from " << path << std::endl;
-
-		try {
-			plugin = std::make_shared<Plugin>(name, path, m_pluginConf[name]);
-			break;
-		} catch (const std::exception &ex) {
-			log::info() << "    error: " << ex.what() << std::endl;
-		}
-	}
-
-	if (plugin)
-		addPlugin(std::move(plugin));
-	else
-		throw std::runtime_error("no suitable plugin found");
-}
-
-void Irccd::reloadPlugin(const std::string &name)
-{
-	auto plugin = getPlugin(name);
-
-	if (plugin)
-		plugin->onReload();
-}
-
-void Irccd::unloadPlugin(const std::string &name)
-{
-	auto plugin = getPlugin(name);
-
-	if (plugin) {
-		plugin->onUnload();
-		m_plugins.erase(name);
-	}
-}
-
-#endif // !WITH_JS
-
-/*
- * Timer slots
- * ------------------------------------------------------------------
- *
- * These slots are called from timer threads.
- */
-
-#if defined(WITH_JS)
-
-void Irccd::handleTimerSignal(std::weak_ptr<Plugin> ptr, std::shared_ptr<Timer> timer)
-{
-	post([this, ptr, timer] () {
-		auto plugin = ptr.lock();
-
-		if (!plugin)
-			return;
-
-		auto &ctx = plugin->context();
-
-		js::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();
-		} catch (const std::exception &) {
-			log::info() << "failure" << std::endl;
-		}
-	});
-}
-
-void Irccd::handleTimerEnd(std::weak_ptr<Plugin> ptr, std::shared_ptr<Timer> timer)
-{
-	post([this, ptr, timer] () {
-		auto plugin = ptr.lock();
-
-		if (plugin) {
-			log::debug() << "timer: finished, removing from plugin `" << plugin->info().name << "'" << std::endl;
-			plugin->removeTimer(timer);
-		}
-	});
-}
-
-#endif
-
-void Irccd::run()
-{
-	while (m_running) {
-		poll();
-		dispatch();
-	}
-}
-
-void Irccd::poll()
-{
-	fd_set setinput;
-	fd_set setoutput;
-	auto max = m_socketServer.handle();
-	auto set = [&] (fd_set &set, net::Handle handle) {
-		FD_SET(handle, &set);
-
-		if (handle > max)
-			max = handle;
-	};
-
-	FD_ZERO(&setinput);
-	FD_ZERO(&setoutput);
-
-	/* 1. Add master socket */
-	FD_SET(m_socketServer.handle(), &setinput);
-
-	/* 2. Add servers */
-	for (auto &pair : m_servers) {
-		pair.second->update();
-		pair.second->prepare(setinput, setoutput, max);
-	}
-
-	/* 3. Add transports clients */
-	for (auto &pair : m_lookupTransportClients) {
-		set(setinput, pair.first);
-
-		if (pair.second->hasOutput())
-			set(setoutput, pair.first);
-	}
-
-	/* 4. Add transport servers */
-	for (auto &pair : m_lookupTransportServers)
-		set(setinput, pair.first);
-
-	/* 5. Do the selection */
-	struct timeval tv;
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 250000;
-
-	int error = select(max + 1, &setinput, &setoutput, nullptr, &tv);
-
-	/* Skip anyway */
-	if (!m_running)
-		return;
-
-	/* Skip on error */
-	if (error < 0 && errno != EINTR) {
-		log::warning() << "irccd: " << net::error(error) << endl;
-		return;
-	}
-
-	process(setinput, setoutput);
-}
-
-void Irccd::dispatch()
-{
-	/*
-	 * Make a copy because the events can add other events while we are iterating it. Also lock because the timers
-	 * may alter these events too.
-	 */
-	std::vector<Event> copy;
-
-	{
-		std::lock_guard<mutex> lock(m_mutex);
-
-		copy = move(m_events);
-
-		/* Clear for safety */
-		m_events.clear();
-	}
-
-	if (copy.size() > 0)
-		log::debug() << "irccd: dispatching " << copy.size() << " event" << (copy.size() > 1 ? "s" : "") << endl;
-
-	for (auto &ev : copy)
-		ev();
-}
-
-void Irccd::stop()
-{
-	log::debug() << "irccd: requesting to stop now" << endl;
-
-	m_running = false;
-}
-
-} // !irccd
--- a/irccd/irccd.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,550 +0,0 @@
-/*
- * irccd.h -- main irccd class
- *
- * 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_H_
-#define _IRCCD_H_
-
-#include <atomic>
-#include <cassert>
-#include <condition_variable>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include <irccd-config.h>
-
-#include <logger.h>
-#include <sockets.h>
-
-#if defined(WITH_JS)
-#  include "plugin.h"
-#endif
-
-#include "rule.h"
-#include "server.h"
-#include "transport-command.h"
-#include "transport-server.h"
-
-namespace irccd {
-
-class Plugin;
-class TransportCommand;
-
-/**
- * Event to execute after the poll.
- */
-using Event = std::function<void ()>;
-
-/**
- * List of events.
- */
-using Events = std::vector<Event>;
-
-/**
- * Map of identities.
- */
-using Identities = std::unordered_map<std::string, ServerIdentity>;
-
-/**
- * List of rules.
- */
-using Rules = std::vector<Rule>;
-
-/**
- * @class ServerEvent
- * @brief Structure that owns several informations about an IRC event
- *
- * This structure is used to dispatch the IRC event to the plugins and the transports.
- */
-class ServerEvent {
-public:
-	std::string server;
-	std::string origin;
-	std::string target;
-	std::string json;
-#if defined(WITH_JS)
-	std::function<std::string (Plugin &)> name;
-	std::function<void (Plugin &)> exec;
-#endif
-};
-
-/**
- * Map of servers.
- */
-using Servers = std::unordered_map<std::string, std::shared_ptr<Server>>;
-
-/**
- * Map of transport command handlers.
- */
-using TransportCommands = std::unordered_map<std::string, std::unique_ptr<TransportCommand>>;
-
-#if defined(WITH_JS)
-
-/**
- * Map of plugins.
- */
-using Plugins = std::unordered_map<std::string, std::shared_ptr<Plugin>>;
-
-/**
- * Map of plugin configurations.
- */
-using PluginConfigs = std::unordered_map<std::string, PluginConfig>;
-
-#endif
-
-/**
- * @class Irccd
- * @brief Irccd main instance
- *
- * This class is used as the main application event loop, it stores servers, plugins and transports.
- *
- * In a general manner, no code in irccd is thread-safe because irccd is mono-threaded except the JavaScript timer
- * API.
- *
- * If you plan to add more threads to irccd, then the simpliest and safest way to execute thread-safe code is to
- * register an event using Irccd::post function which will be called during the event loop dispatching.
- *
- * Thus, except noticed as thread-safe, no function is assumed to be.
- */
-class Irccd {
-private:
-	template <typename T>
-	using LookupTable = std::unordered_map<net::Handle, std::shared_ptr<T>>;
-
-	/* Main loop */
-	std::atomic<bool> m_running{true};
-
-	/* Mutex for post() */
-	std::mutex m_mutex;
-
-	/* IPC */
-	net::SocketTcp<net::address::Ip> m_socketServer;
-	net::SocketTcp<net::address::Ip> m_socketClient;
-
-	/* Event loop */
-	Events m_events;
-
-	/* Servers */
-	Servers m_servers;
-
-	/* Optional JavaScript plugins */
-#if defined(WITH_JS)
-	Plugins m_plugins;
-	PluginConfigs m_pluginConf;
-#endif
-
-	/* Identities */
-	Identities m_identities;
-
-	/* Rules */
-	Rules m_rules;
-
-	/* Lookup tables */
-	LookupTable<TransportClient> m_lookupTransportClients;
-	LookupTable<TransportServer> m_lookupTransportServers;
-
-	/* Transport commands handlers */
-	TransportCommands m_transportCommands;
-
-	/*
-	 * Server slots
-	 * ----------------------------------------------------------
-	 */
-
-	void handleServerChannelMode(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg);
-	void handleServerChannelNotice(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string notice);
-	void handleServerConnect(std::weak_ptr<Server> server);
-	void handleServerInvite(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target);
-	void handleServerJoin(std::weak_ptr<Server> server, std::string origin, std::string channel);
-	void handleServerKick(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason);
-	void handleServerMessage(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string message);
-	void handleServerMe(std::weak_ptr<Server> server, std::string origin, std::string target, std::string message);
-	void handleServerMode(std::weak_ptr<Server> server, std::string origin, std::string mode);
-	void handleServerNames(std::weak_ptr<Server> server, std::string channel, std::set<std::string> nicknames);
-	void handleServerNick(std::weak_ptr<Server> server, std::string origin, std::string nickname);
-	void handleServerNotice(std::weak_ptr<Server> server, std::string origin, std::string message);
-	void handleServerPart(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string reason);
-	void handleServerQuery(std::weak_ptr<Server> server, std::string origin, std::string message);
-	void handleServerTopic(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string topic);
-	void handleServerWhois(std::weak_ptr<Server> server, ServerWhois whois);
-
-	/*
-	 * Transport clients slots
-	 * ----------------------------------------------------------
-	 */
-	void handleTransportCommand(std::weak_ptr<TransportClient>, const json::Value &);
-	void handleTransportDie(std::weak_ptr<TransportClient>);
-
-	/*
-	 * Plugin timers slots
-	 * ----------------------------------------------------------
-	 *
-	 * These handlers catch the timer signals and call the plugin function or remove the timer from the plugin.
-	 */
-
-#if defined(WITH_JS)
-	void handleTimerSignal(std::weak_ptr<Plugin>, std::shared_ptr<Timer>);
-	void handleTimerEnd(std::weak_ptr<Plugin>, std::shared_ptr<Timer>);
-#endif
-
-	/*
-	 * Process the socket sets.
-	 * ----------------------------------------------------------
-	 *
-	 * These functions are called after polling which sockets are ready for reading/writing.
-	 */
-
-	void processIpc(fd_set &input);
-	void processTransportClients(fd_set &input, fd_set &output);
-	void processTransportServers(fd_set &input);
-	void processServers(fd_set &input, fd_set &output);
-	void process(fd_set &setinput, fd_set &setoutput);
-
-public:
-	/**
-	 * Constructor that instanciate IPC.
-	 */
-	Irccd();
-
-	/**
-	 * Load a configuration into irccd. Added as convenience to allow expressions like `irccd.load(Config{"foo"})`.
-	 *
-	 * @param config the configuration loader
-	 */
-	template <typename T>
-	inline void load(T &&config)
-	{
-		config.load(*this);
-	}
-
-	/**
-	 * Add an event to the queue. This will immediately signals the event loop to interrupt itself to dispatch
-	 * the pending events.
-	 *
-	 * @param ev the event
-	 * @note Thread-safe
-	 */
-	void post(Event ev) noexcept;
-
-	/*
-	 * This function wraps post() to iterate over all plugins to call the function and to send to all
-	 * connected transport the event.
-	 */
-	void postServerEvent(ServerEvent) noexcept;
-
-	/*
-	 * Identity management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions to get or add new identities.
-	 */
-
-	/**
-	 * Add an identity.
-	 *
-	 * @param identity the identity
-	 * @note If the identity already exists, it is overriden
-	 */
-	inline void addIdentity(ServerIdentity identity) noexcept
-	{
-		m_identities.emplace(identity.name, std::move(identity));
-	}
-
-	/**
-	 * Get an identity, if not found, the default one is used.
-	 *
-	 * @param name the identity name
-	 * @return the identity or default one
-	 */
-	inline ServerIdentity findIdentity(const std::string &name) const noexcept
-	{
-		auto it = m_identities.find(name);
-
-		return it == m_identities.end() ? ServerIdentity() : it->second;
-	}
-
-	/*
-	 * Server management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions to get or create new servers.
-	 *
-	 * Servers that are added to this instance are automatically polled when run() is called.
-	 */
-
-	/**
-	 * Check if a server exists.
-	 *
-	 * @param name the name
-	 * @return true if exists
-	 */
-	inline bool hasServer(const std::string &name) const noexcept
-	{
-		return m_servers.count(name) > 0;
-	}
-
-	/**
-	 * Add a new server to the application.
-	 *
-	 * @pre hasServer must return false
-	 * @param sv the server
-	 */
-	void addServer(std::shared_ptr<Server> sv) noexcept;
-
-	/**
-	 * Get a server or empty one if not found
-	 *
-	 * @param name the server name
-	 * @return the server or empty one if not found
-	 */
-	std::shared_ptr<Server> getServer(const std::string &name) const noexcept;
-
-	/**
-	 * Find a server by name.
-	 *
-	 * @param name the server name
-	 * @return the server
-	 * @throw std::out_of_range if the server does not exist
-	 */
-	std::shared_ptr<Server> requireServer(const std::string &name) const;
-
-	/**
-	 * Get the map of loaded servers.
-	 *
-	 * @return the servers
-	 */
-	inline const Servers &servers() const noexcept
-	{
-		return m_servers;
-	}
-
-	/**
-	 * Remove a server from the irccd instance.
-	 *
-	 * The server if any, will be disconnected.
-	 *
-	 * @param name the server name
-	 */
-	void removeServer(const std::string &name);
-
-	/**
-	 * Remove all servers.
-	 *
-	 * All servers will be disconnected.
-	 */
-	void clearServers() noexcept;
-
-	/*
-	 * Transport management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions for adding new transport servers.
-	 */
-
-	/**
-	 * Add a transport server.
-	 *
-	 * @param ts the transport server
-	 */
-	void addTransport(std::shared_ptr<TransportServer> ts);
-
-	/**
-	 * Register a new transport command.
-	 *
-	 * @param key the transport command name
-	 */
-	template <typename Cmd>
-	inline void addTransportCommand(std::string key)
-	{
-		m_transportCommands.emplace(std::move(key), std::make_unique<Cmd>());
-	}
-
-	/*
-	 * Plugin management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions for loading JavaScript plugins.
-	 */
-
-#if defined(WITH_JS)
-	/**
-	 * Check if a plugin is loaded.
-	 *
-	 * @param name the plugin id
-	 * @return true if has plugin
-	 */
-	inline bool hasPlugin(const std::string &name) const noexcept
-	{
-		return m_plugins.count(name) > 0;
-	}
-
-	/**
-	 * Get a plugin or empty one if not found.
-	 *
-	 * @param name the plugin id
-	 * @return the plugin or empty one if not found
-	 */
-	std::shared_ptr<Plugin> getPlugin(const std::string &name) const noexcept;
-
-	/**
-	 * Find a plugin.
-	 *
-	 * @param name the plugin id
-	 * @return the plugin
-	 * @throws std::out_of_range if not found
-	 */
-	std::shared_ptr<Plugin> requirePlugin(const std::string &name) const;
-
-	/**
-	 * Add plugin configuration for the specified plugin.
-	 *
-	 * @param name
-	 * @param config
-	 */
-	inline void addPluginConfig(std::string name, PluginConfig config)
-	{
-		m_pluginConf.emplace(std::move(name), std::move(config));
-	}
-
-	/**
-	 * Add a loaded plugin.
-	 *
-	 * Plugins signals will be connected to the irccd main loop. The onLoad function will also be called and the
-	 * plugin is not added on errors.
-	 *
-	 * @pre plugin must not be empty
-	 * @param plugin the plugin
-	 */
-	void addPlugin(std::shared_ptr<Plugin> plugin);
-
-	/**
-	 * Load a plugin by path or by searching through directories.
-	 *
-	 * TODO: Move this somewhere else (e.g. Plugin::find).
-	 *
-	 * @param source the path or the plugin id to search
-	 * @param find set to true for searching by id
-	 */
-	void loadPlugin(std::string name, const std::string &source, bool find);
-
-	/**
-	 * Unload a plugin and remove it.
-	 *
-	 * @param name the plugin id
-	 */
-	void unloadPlugin(const std::string &name);
-
-	/**
-	 * Reload a plugin by calling onReload.
-	 *
-	 * @param name the plugin name
-	 * @throw std::exception on failures
-	 */
-	void reloadPlugin(const std::string &name);
-
-	/**
-	 * Get the map of plugins.
-	 *
-	 * @return the map of plugins
-	 */
-	inline const Plugins &plugins() const noexcept
-	{
-		return m_plugins;
-	}
-
-#endif // !WITH_JS
-
-	/*
-	 * Rule management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions for adding, creating new rules that are used to filter IRC events before being processed
-	 * by JavaScript plugins.
-	 */
-
-	/**
-	 * Append a rule.
-	 *
-	 * @param rule the rule to append
-	 */
-	inline void addRule(Rule rule)
-	{
-		m_rules.push_back(std::move(rule));
-	}
-
-	/**
-	 * Insert a new rule at the specified position.
-	 *
-	 * @param rule the rule
-	 * @param position the position
-	 */
-	inline void insertRule(Rule rule, unsigned position)
-	{
-		assert(position <= m_rules.size());
-
-		m_rules.insert(m_rules.begin() + position, std::move(rule));
-	}
-
-	/**
-	 * Get the list of rules.
-	 *
-	 * @return the list of rules
-	 */
-	inline const std::vector<Rule> &rules() const noexcept
-	{
-		return m_rules;
-	}
-
-	/**
-	 * Remove a new rule from the specified position.
-	 *
-	 * @param rule the rule
-	 * @param position the position
-	 */
-	inline void removeRule(unsigned position)
-	{
-		assert(position < m_rules.size());
-
-		m_rules.erase(m_rules.begin() + position);
-	}
-
-	/**
-	 * Loop forever by calling poll() and dispatch() indefinitely.
-	 */
-	void run();
-
-	/**
-	 * Poll the next events without blocking (250 ms max).
-	 */
-	void poll();
-
-	/**
-	 * Dispatch the pending events, usually after calling poll().
-	 */
-	void dispatch();
-
-	/**
-	 * Request to stop, usually from a signal.
-	 */
-	void stop();
-};
-
-} // !irccd
-
-#endif // !_IRCCD_H_
--- a/irccd/js-directory.cpp	Mon Mar 14 11:24:25 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 <directory.h>
-#include <filesystem.h>
-#include <path.h>
-
-#include "js.h"
-#include "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/irccd/js-directory.h	Mon Mar 14 11:24:25 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/irccd/js-elapsed-timer.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +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 <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/irccd/js-elapsed-timer.h	Mon Mar 14 11:24:25 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/irccd/js-file.cpp	Mon Mar 14 11:24:25 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 <filesystem.h>
-
-#include "js-irccd.h"
-#include "js-file.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/irccd/js-file.h	Mon Mar 14 11:24:25 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/irccd/js-irccd.cpp	Mon Mar 14 11:24:25 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 "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/irccd/js-irccd.h	Mon Mar 14 11:24:25 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/irccd/js-logger.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +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 <logger.h>
-
-#include "js-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/irccd/js-logger.h	Mon Mar 14 11:24:25 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/irccd/js-plugin.cpp	Mon Mar 14 11:24:25 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.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>
-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/irccd/js-plugin.h	Mon Mar 14 11:24:25 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/irccd/js-server.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,539 +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.h"
-#include "js.h"
-#include "server.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/irccd/js-server.h	Mon Mar 14 11:24:25 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/irccd/js-system.cpp	Mon Mar 14 11:24:25 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 <system.h>
-
-#include "js-file.h"
-#include "js-irccd.h"
-#include "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/irccd/js-system.h	Mon Mar 14 11:24:25 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/irccd/js-timer.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +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 "js.h"
-#include "plugin.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/irccd/js-timer.h	Mon Mar 14 11:24:25 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/irccd/js-unicode.cpp	Mon Mar 14 11:24:25 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 <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/irccd/js-unicode.h	Mon Mar 14 11:24:25 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/irccd/js-util.cpp	Mon Mar 14 11:24:25 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 <util.h>
-
-#include "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/irccd/js-util.h	Mon Mar 14 11:24:25 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/irccd/js.cpp	Mon Mar 14 11:24:25 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/irccd/js.h	Mon Mar 14 11:24:25 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/irccd/main.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ b/irccd/main.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -20,35 +20,13 @@
 
 #include <irccd-config.h>
 
-#include <logger.h>
-#include <options.h>
-#include <path.h>
-#include <system.h>
+#include <irccd/logger.h>
+#include <irccd/options.h>
+#include <irccd/path.h>
+#include <irccd/system.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 "config.h"
-#include "irccd.h"
+#include <irccd/config.h>
+#include <irccd/irccd.h>
 
 using namespace irccd;
 
@@ -63,6 +41,8 @@
 
 void init(int &argc, char **&argv)
 {
+	// MOVE THIS IN Application
+
 	/* Needed for some components */
 	sys::setProgramName("irccd");
 	path::setApplicationPath(argv[0]);
@@ -128,28 +108,7 @@
 	}
 
 	instance = std::make_unique<Irccd>();
-	instance->addTransportCommand<command::PluginInfo>("plugin-info");
-	instance->addTransportCommand<command::PluginList>("plugin-list");
-	instance->addTransportCommand<command::PluginLoad>("plugin-load");
-	instance->addTransportCommand<command::PluginReload>("plugin-reload");
-	instance->addTransportCommand<command::PluginUnload>("plugin-unload");
-	instance->addTransportCommand<command::ServerChannelMode>("server-cmode");
-	instance->addTransportCommand<command::ServerChannelNotice>("server-cnotice");
-	instance->addTransportCommand<command::ServerConnect>("server-connect");
-	instance->addTransportCommand<command::ServerDisconnect>("server-disconnect");
-	instance->addTransportCommand<command::ServerInfo>("server-info");
-	instance->addTransportCommand<command::ServerInvite>("server-invite");
-	instance->addTransportCommand<command::ServerJoin>("server-join");
-	instance->addTransportCommand<command::ServerKick>("server-kick");
-	instance->addTransportCommand<command::ServerList>("server-list");
-	instance->addTransportCommand<command::ServerMe>("server-me");
-	instance->addTransportCommand<command::ServerMessage>("server-message");
-	instance->addTransportCommand<command::ServerMode>("server-mode");
-	instance->addTransportCommand<command::ServerNick>("server-nick");
-	instance->addTransportCommand<command::ServerNotice>("server-notice");
-	instance->addTransportCommand<command::ServerPart>("server-part");
-	instance->addTransportCommand<command::ServerReconnect>("server-reconnect");
-	instance->addTransportCommand<command::ServerTopic>("server-topic");
+
 	instance->load(Config{result});
 	instance->run();
 
--- a/irccd/plugin.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,482 +0,0 @@
-/*
- * plugin.cpp -- irccd JavaScript plugin interface
- *
- * 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 <stdexcept>
-
-#include <irccd-config.h>
-
-#if defined(HAVE_STAT)
-#  include <sys/stat.h>
-#  include <cerrno>
-#  include <cstring>
-#endif
-
-#include <filesystem.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"
-
-using namespace std;
-
-namespace irccd {
-
-void Plugin::call(const string &name, unsigned nargs)
-{
-	m_context.getGlobal<void>(name);
-
-	if (m_context.type(-1) == DUK_TYPE_UNDEFINED) {
-		/* Function not defined, remove the undefined value and all arguments */
-		m_context.pop(nargs + 1);
-	} else {
-		/* Call the function and discard the result */
-		m_context.insert(-nargs - 1);
-		m_context.pcall(nargs);
-		m_context.pop();
-	}
-}
-
-void Plugin::putVars()
-{
-	js::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);
-}
-
-void Plugin::putPath(const std::string &varname, const std::string &append, path::Path type)
-{
-	js::StackAssert sa(m_context);
-
-	bool found = true;
-	std::string foundpath;
-
-	/*
-	 * Use the first existing directory available.
-	 */
-	for (const std::string &p : path::list(type)) {
-		foundpath = path::clean(p + append);
-
-		if (fs::exists(foundpath)) {
-			found = true;
-			break;
-		}
-	}
-
-	/* Use the system as default */
-	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);
-}
-
-void Plugin::putPaths()
-{
-	js::StackAssert sa(m_context);
-
-	/*
-	 * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/)
-	 * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/)
-	 */
-	putPath("dataPath", "plugin/" + m_info.name, path::PathData);
-	putPath("configPath", "plugin/" + m_info.name, path::PathConfig);
-	putPath("cachePath", "plugin/" + m_info.name, path::PathCache);
-}
-
-void Plugin::putConfig(const PluginConfig &config)
-{
-	js::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");
-
-	if (m_context.type(-1) != DUK_TYPE_OBJECT) {
-		m_context.pop();
-		m_context.push(js::Object{});
-	}
-
-	m_context.push(config);
-	m_context.putProperty(-2, "config");
-	m_context.pop(2);
-}
-
-Plugin::Plugin(std::string name, std::string path, const PluginConfig &config)
-{
-	js::StackAssert sa(m_context);
-
-	m_info.name = std::move(name);
-	m_info.path = std::move(path);
-
-	/*
-	 * Duktape currently emit useless warnings when a file do
-	 * not exists so we do a homemade access.
-	 */
-#if defined(HAVE_STAT)
-	struct stat st;
-
-	if (stat(m_info.path.c_str(), &st) < 0)
-		throw std::runtime_error(std::strerror(errno));
-#endif
-
-	/*
-	 * Store the base path to the plugin, it is required for
-	 * Duktape.modSearch to find external modules and other
-	 * sources.
-	 *
-	 * If path is absolute, the parent is the directory name, otherwise
-	 * we use the current working directory (needed for some tests).
-	 */
-	if (fs::isAbsolute(m_info.path))
-		m_info.parent = fs::dirName(m_info.path);
-	else
-		m_info.parent = fs::cwd();
-
-	/* Load standard irccd API */
-	loadJsIrccd(m_context);
-	loadJsDirectory(m_context);
-	loadJsElapsedTimer(m_context);
-	loadJsFile(m_context);
-	loadJsLogger(m_context);
-	loadJsPlugin(m_context);
-	loadJsServer(m_context);
-	loadJsSystem(m_context);
-	loadJsTimer(m_context);
-	loadJsUnicode(m_context);
-	loadJsUtil(m_context);
-
-	putVars();
-	putPaths();
-
-	/* Try to load the file (does not call onLoad yet) */
-	m_context.peval(js::File{m_info.path});
-	m_context.pop();
-
-	/* Initialize user defined options after loading to allow the plugin to define default values */
-	putConfig(config);
-
-	/* Read metadata */
-	m_context.getGlobal<void>("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");
-	}
-
-	m_context.pop();
-
-	log::debug() << "plugin " << m_info.name << ": " << std::endl;
-	log::debug() << "  author:  " << m_info.author << std::endl;
-	log::debug() << "  license: " << m_info.license << std::endl;
-	log::debug() << "  summary: " << m_info.summary << std::endl;
-	log::debug() << "  version: " << m_info.version << std::endl;
-}
-
-const PluginInfo &Plugin::info() const
-{
-	return m_info;
-}
-
-void Plugin::addTimer(std::shared_ptr<Timer> timer) noexcept
-{
-	std::weak_ptr<Timer> ptr(timer);
-
-	/*
-	 * These signals are called from the Timer thread and are transmitted to irccd so that it can
-	 * calls appropriate timer functions.
-	 */
-	timer->onSignal.connect([this, ptr] () {
-		auto timer = ptr.lock();
-
-		if (timer)
-			onTimerSignal(move(timer));
-	});
-	timer->onEnd.connect([this, ptr] () {
-		auto timer = ptr.lock();
-
-		if (timer)
-			onTimerEnd(move(timer));
-	});
-
-	m_timers.insert(move(timer));
-}
-
-void Plugin::removeTimer(const std::shared_ptr<Timer> &timer) noexcept
-{
-	/* 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())));
-
-	/* Remove from list */
-	m_timers.erase(timer);
-}
-
-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);
-
-	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));
-	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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(message));
-	call("onCommand", 4);
-}
-
-void Plugin::onConnect(std::shared_ptr<Server> server)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	call("onInvite", 3);
-}
-
-void Plugin::onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(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);
-
-	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));
-	call("onKick", 5);
-}
-
-void Plugin::onLoad()
-{
-	js::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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(message));
-	call("onMe", 4);
-}
-
-void Plugin::onMode(std::shared_ptr<Server> server, std::string origin, std::string mode)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(channel));
-	m_context.push(move(names));
-	call("onNames", 3);
-}
-
-void Plugin::onNick(std::shared_ptr<Server> server, std::string oldnick, std::string newnick)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(oldnick));
-	m_context.push(move(newnick));
-	call("onNick", 3);
-}
-
-void Plugin::onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(reason));
-	call("onPart", 4);
-}
-
-void Plugin::onQuery(std::shared_ptr<Server> server, std::string origin, std::string message)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(message));
-	call("onQuery", 3);
-}
-
-void Plugin::onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message)
-{
-	js::StackAssert sa(m_context);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(message));
-	call("onQueryCommand", 3);
-}
-
-void Plugin::onReload()
-{
-	js::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);
-
-	m_context.push(js::Shared<Server>{server});
-	m_context.push(move(origin));
-	m_context.push(move(channel));
-	m_context.push(move(topic));
-	call("onTopic", 4);
-}
-
-void Plugin::onUnload()
-{
-	js::StackAssert sa(m_context);
-
-	call("onUnload");
-}
-
-void Plugin::onWhois(std::shared_ptr<Server> server, ServerWhois whois)
-{
-	js::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);
-	call("onWhois", 2);
-}
-
-namespace js {
-
-void TypeInfo<PluginInfo>::push(Context &ctx, const PluginInfo &info)
-{
-	js::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);
-}
-
-} // !js
-
-} // !irccd
--- a/irccd/plugin.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,356 +0,0 @@
-/*
- * plugin.h -- irccd JavaScript plugin interface
- *
- * 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_H_
-#define _IRCCD_PLUGIN_H_
-
-/**
- * @file Plugin.h
- * @brief Irccd plugins
- */
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include <path.h>
-#include <signals.h>
-
-#include "js.h"
-#include "timer.h"
-
-namespace irccd {
-
-class Server;
-class ServerWhois;
-
-/**
- * @class PluginInfo
- * @brief Plugin information
- */
-class PluginInfo {
-public:
-	std::string name;		//!< plugin name (from file on disk)
-	std::string parent;		//!< parent directory
-	std::string path;		//!< full path to the plugin file
-
-	/* Metadata */
-	std::string author{"unknown"};	//!< plugin author
-	std::string license{"unknown"};	//!< plugin license
-	std::string summary{"unknown"};	//!< short plugin description
-	std::string version{"unknown"};	//!< plugin version
-};
-
-/**
- * Configuration map extract from config file.
- */
-using PluginConfig = std::unordered_map<std::string, std::string>;
-
-/**
- * Timers that a plugin owns.
- */
-using PluginTimers = std::unordered_set<std::shared_ptr<Timer>>;
-
-/**
- * @class Plugin
- * @brief JavaScript plugin
- *
- * A plugin is identified by name and can be loaded and unloaded
- * at runtime.
- */
-class Plugin {
-public:
-	/**
-	 * Signal: onTimerSignal
-	 * ------------------------------------------------
-	 *
-	 * When a timer expires.
-	 *
-	 * Arguments:
-	 * - the timer object
-	 */
-	Signal<std::shared_ptr<Timer>> onTimerSignal;
-
-	/**
-	 * Signal: onTimerEnd
-	 * ------------------------------------------------
-	 *
-	 * When a timer is finished.
-	 *
-	 * Arguments:
-	 * - the timer object
-	 */
-	Signal<std::shared_ptr<Timer>> onTimerEnd;
-
-private:
-	/* JavaScript context */
-	js::Context m_context;
-
-	/* Plugin info and its timers */
-	PluginInfo m_info;
-	PluginTimers m_timers;
-
-	/* Private helpers */
-	void call(const std::string &name, unsigned nargs = 0);
-	void putVars();
-	void putPath(const std::string &varname, const std::string &append, path::Path type);
-	void putPaths();
-	void putConfig(const PluginConfig &config);
-
-public:
-	/**
-	 * Correct constructor.
-	 *
-	 * @param name the plugin id
-	 * @param path the fully resolved path to the plugin
-	 * @param config the plugin configuration
-	 * @throws std::runtime_error on errors
-	 */
-	Plugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
-
-	/**
-	 * Get the plugin information.
-	 */
-	const PluginInfo &info() const;
-
-	/**
-	 * Add a timer to the plugin.
-	 *
-	 * @param timer the timer to add
-	 */
-	void addTimer(std::shared_ptr<Timer> timer) noexcept;
-
-	/**
-	 * Remove a timer from a plugin.
-	 *
-	 * @param timer
-	 */
-	void removeTimer(const std::shared_ptr<Timer> &timer) noexcept;
-
-	/**
-	 * Access the Duktape context.
-	 *
-	 * @return the context
-	 */
-	inline js::Context &context() noexcept
-	{
-		return m_context;
-	}
-
-	/**
-	 * On channel message. This event will call onMessage or
-	 * onCommand if the messages starts with the command character
-	 * plus the plugin name.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the message
-	 * @param channel the channel
-	 * @param message the message or command
-	 */
-	void onCommand(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
-
-	/**
-	 * On successful connection.
-	 *
-	 * @param server the server
-	 */
-	void onConnect(std::shared_ptr<Server> server);
-
-	/**
-	 * On channel mode.
-	 *
-	 * @param server the server
-	 * @param origin the ouser who has changed the mode
-	 * @param channel the channel
-	 * @param mode the mode
-	 * @param arg the optional mode argument
-	 */
-	void onChannelMode(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg);
-
-	/**
-	 * On a channel notice.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the notice
-	 * @param channel on which channel
-	 * @param notice the message
-	 */
-	void onChannelNotice(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string notice);
-
-	/**
-	 * On invitation.
-	 *
-	 * @param server the server
-	 * @param origin the user who invited you
-	 * @param channel the channel
-	 */
-	void onInvite(std::shared_ptr<Server> server, std::string origin, std::string channel);
-
-	/**
-	 * On join.
-	 *
-	 * @param server the server
-	 * @param origin the user who joined
-	 * @param channel the channel
-	 */
-	void onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel);
-
-	/**
-	 * On kick.
-	 *
-	 * @param server the server
-	 * @param origin the user who kicked the target
-	 * @param channel the channel
-	 * @param target the kicked target
-	 * @param reason the optional reason
-	 */
-	void onKick(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason);
-
-	/**
-	 * On load.
-	 */
-	void onLoad();
-
-	/**
-	 * On channel message.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the message
-	 * @param channel the channel
-	 * @param message the message or command
-	 */
-	void onMessage(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
-
-	/**
-	 * On CTCP Action.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the message
-	 * @param channel the channel (may also be your nickname)
-	 * @param message the message
-	 */
-	void onMe(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
-
-	/**
-	 * On user mode change.
-	 *
-	 * @param server the server
-	 * @param origin the person who changed the mode
-	 * @param mode the new mode
-	 */
-	void onMode(std::shared_ptr<Server> server, std::string origin, std::string mode);
-
-	/**
-	 * On names listing.
-	 *
-	 * @param server the server
-	 * @param channel the channel
-	 * @param list the list of nicknames
-	 */
-	void onNames(std::shared_ptr<Server> server, std::string channel, std::vector<std::string> list);
-
-	/**
-	 * On nick change.
-	 *
-	 * @param server the server
-	 * @param origin the user that changed its nickname
-	 * @param nick the new nickname
-	 */
-	void onNick(std::shared_ptr<Server> server, std::string origin, std::string nick);
-
-	/**
-	 * On user notice.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the notice
-	 * @param notice the notice
-	 */
-	void onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice);
-
-	/**
-	 * On part.
-	 *
-	 * @param server the server
-	 * @param origin the user who left
-	 * @param channel the channel
-	 * @param reason the optional reason
-	 */
-	void onPart(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string reason);
-
-	/**
-	 * On user query.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the query
-	 * @param message the message
-	 */
-	void onQuery(std::shared_ptr<Server> server, std::string origin, std::string message);
-
-	/**
-	 * On user query command.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the query
-	 * @param message the message
-	 */
-	void onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message);
-
-	/**
-	 * On reload.
-	 */
-	void onReload();
-
-	/**
-	 * On topic change.
-	 *
-	 * @param server the server
-	 * @param origin the user who sent the topic
-	 * @param channel the channel
-	 * @param topic the new topic
-	 */
-	void onTopic(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string topic);
-
-	/**
-	 * On unload.
-	 */
-	void onUnload();
-
-	/**
-	 * On whois information.
-	 *
-	 * @param server the server
-	 * @param info the info
-	 */
-	void onWhois(std::shared_ptr<Server> server, ServerWhois info);
-};
-
-namespace js {
-
-template <>
-class TypeInfo<PluginInfo> {
-public:
-	static void push(Context &ctx, const PluginInfo &info);
-};
-
-} // !js
-
-} // !irccd
-
-#endif // !_IRCCD_PLUGIN_H_
--- a/irccd/rule.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * rule.cpp -- rule for server and channels
- *
- * 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 <stdexcept>
-
-#include <logger.h>
-#include <util.h>
-
-#include "rule.h"
-
-using namespace std;
-
-namespace {
-
-const std::unordered_set<std::string> validEvents{
-	"onChannelMode"
-	"onChannelNotice",
-	"onCommand",
-	"onConnect",
-	"onInvite",
-	"onJoin",
-	"onKick",
-	"onMessage",
-	"onMode",
-	"onNames",
-	"onNick",
-	"onNotice",
-	"onPart",
-	"onQuery",
-	"onQueryCommand",
-	"onTopic",
-	"onWhois"
-};
-
-} // !namespace
-
-namespace irccd {
-
-bool Rule::solve(const std::vector<Rule> &rules,
-		 const std::string &server,
-		 const std::string &channel,
-		 const std::string &origin,
-		 const std::string &plugin,
-		 const std::string &event) noexcept
-{
-	bool result = true;
-
-	log::debug() << "rule: solving for:\n"
-		     << "  server: " << server << "\n"
-		     << "  channel: " << channel << "\n"
-		     << "  origin: " << origin << "\n"
-		     << "  plugin: " << plugin << "\n"
-		     << "  event: " << event << std::endl;
-
-	int i = 0;
-	for (const Rule &rule : rules) {
-		log::debug() << "  candidate " << i++ << ":\n"
-			     << "    servers: " << util::join(rule.m_servers.begin(), rule.m_servers.end()) << "\n"
-			     << "    channels: " << util::join(rule.m_channels.begin(), rule.m_channels.end()) << "\n"
-			     << "    origins: " << util::join(rule.m_origins.begin(), rule.m_origins.end()) << "\n"
-			     << "    plugins: " << util::join(rule.m_plugins.begin(), rule.m_plugins.end()) << "\n"
-			     << "    events: " << util::join(rule.m_events.begin(), rule.m_events.end()) << "\n"
-			     << "    action: " << ((rule.m_action == RuleAction::Accept) ? "accept" : "drop") << std::endl;
-
-		if (rule.match(server, channel, origin, plugin, event))
-			result = rule.action() == RuleAction::Accept;
-	}
-
-	return result;
-}
-
-bool Rule::matchMap(const RuleSet &map, const std::string &value) const noexcept
-{
-	return value.empty() || map.empty() || map.count(value) == 1;
-}
-
-Rule::Rule(RuleSet servers, RuleSet channels, RuleSet origins, RuleSet plugins, RuleSet events, RuleAction action)
-	: m_servers(std::move(servers))
-	, m_channels(std::move(channels))
-	, m_origins(std::move(origins))
-	, m_plugins(std::move(plugins))
-	, m_events(std::move(events))
-	, m_action(action)
-{
-	for (const std::string &n : m_events)
-		if (validEvents.count(n) == 0)
-			throw std::invalid_argument(n + " is not a valid event name");
-}
-
-bool Rule::match(const std::string &server,
-		 const std::string &channel,
-		 const std::string &nick,
-		 const std::string &plugin,
-		 const std::string &event) const noexcept
-{
-	return matchMap(m_servers, server) &&
-	       matchMap(m_channels, channel) &&
-	       matchMap(m_origins, nick) &&
-	       matchMap(m_plugins, plugin) &&
-	       matchMap(m_events, event);
-}
-
-RuleAction Rule::action() const noexcept
-{
-	return m_action;
-}
-
-const RuleSet &Rule::servers() const noexcept
-{
-	return m_servers;
-}
-
-const RuleSet &Rule::channels() const noexcept
-{
-	return m_channels;
-}
-
-const RuleSet &Rule::origins() const noexcept
-{
-	return m_origins;
-}
-
-const RuleSet &Rule::plugins() const noexcept
-{
-	return m_plugins;
-}
-
-const RuleSet &Rule::events() const noexcept
-{
-	return m_events;
-}
-
-} // !irccd
--- a/irccd/rule.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-/*
- * rule.h -- rule for server and channels
- *
- * 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_RULE_H_
-#define _IRCCD_RULE_H_
-
-/**
- * @file Rule.h
- * @brief Rule description
- */
-
-#include <sstream>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-namespace irccd {
-
-/**
- * List of criterias.
- */
-using RuleSet = std::unordered_set<std::string>;
-
-/**
- * @enum RuleAction
- * @brief Rule action
- */
-enum class RuleAction {
-	Accept,			//!< The event is accepted (default)
-	Drop			//!< The event is dropped
-};
-
-/**
- * @class Rule
- * @brief Manage rule to activate or deactive events.
- */
-class Rule final {
-private:
-	RuleSet m_servers;
-	RuleSet m_channels;
-	RuleSet m_origins;
-	RuleSet m_plugins;
-	RuleSet m_events;
-	RuleAction m_action{RuleAction::Accept};
-
-	/*
-	 * Check if a map contains the value and return true if it is
-	 * or return true if value is empty (which means applicable).
-	 */
-	bool matchMap(const RuleSet &map, const std::string &value) const noexcept;
-
-public:
-	/**
-	 * Resolve the action to execute with the specified list of rules.
-	 *
-	 * @param rules the list of rules
-	 * @param server the server name
-	 * @param channel the channel name
-	 * @param origin the origin
-	 * @param plugin the plugin name
-	 * @param event the event name (e.g onKick)
-	 * @return true if the plugin must be called
-	 */
-	static bool solve(const std::vector<Rule> &rules,
-			  const std::string &server,
-			  const std::string &channel,
-			  const std::string &origin,
-			  const std::string &plugin,
-			  const std::string &event) noexcept;
-
-	/**
-	 * Rule constructor.
-	 *
-	 * @param servers the server list
-	 * @param channels the channels
-	 * @param nicknames the nicknames
-	 * @param plugins the plugins
-	 * @param events the events
-	 * @param action the rule action
-	 * @throw std::invalid_argument if events are invalid
-	 */
-	Rule(RuleSet servers = RuleSet{},
-	     RuleSet channels = RuleSet{},
-	     RuleSet nicknames = RuleSet{},
-	     RuleSet plugins = RuleSet{},
-	     RuleSet events = RuleSet{},
-	     RuleAction action = RuleAction::Accept);
-
-	/**
-	 * Check if that rule apply for the given criterias.
-	 *
-	 * @param server the server
-	 * @param channel the channel
-	 * @param nick the origin
-	 * @param plugin the plugin
-	 * @param event the event
-	 * @return true if match
-	 */
-	bool match(const std::string &server,
-		   const std::string &channel,
-		   const std::string &nick,
-		   const std::string &plugin,
-		   const std::string &event) const noexcept;
-
-	/**
-	 * Get the action.
-	 *
-	 * @return the action
-	 */
-	RuleAction action() const noexcept;
-
-	/**
-	 * Get the servers.
-	 *
-	 * @return the servers
-	 */
-	const RuleSet &servers() const noexcept;
-
-	/**
-	 * Get the channels.
-	 *
-	 * @return the channels
-	 */
-	const RuleSet &channels() const noexcept;
-
-	/**
-	 * Get the origins.
-	 *
-	 * @return the origins
-	 */
-	const RuleSet &origins() const noexcept;
-
-	/**
-	 * Get the plugins.
-	 *
-	 * @return the plugins
-	 */
-	const RuleSet &plugins() const noexcept;
-
-	/**
-	 * Get the events.
-	 *
-	 * @return the events
-	 */
-	const RuleSet &events() const noexcept;
-};
-
-} // !irccd
-
-#endif // !_IRCCD_RULE_H_
--- a/irccd/server-state.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * server-state.cpp -- server current state
- *
- * 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 <irccd-config.h>
-
-#if !defined(_WIN32)
-#  include <sys/types.h>
-#  include <netinet/in.h>
-#  include <arpa/nameser.h>
-#  include <resolv.h>
-#endif
-
-#include "server-state.h"
-#include "server.h"
-
-namespace irccd {
-
-bool ServerState::connect(Server &server)
-{
-	const ServerInfo &info = server.info();
-	const ServerIdentity &identity = server.identity();
-	const char *password = info.password.empty() ? nullptr : info.password.c_str();
-	std::string host = info.host;
-	int code;
-
-	/* libircclient requires # for SSL connection */
-#if defined(WITH_SSL)
-	if (info.flags & ServerInfo::Ssl)
-		host.insert(0, 1, '#');
-	if (!(info.flags & ServerInfo::SslVerify))
-		irc_option_set(server.session(), LIBIRC_OPTION_SSL_NO_VERIFY);
-#endif
-
-	if (info.flags & ServerInfo::Ipv6) {
-		code = irc_connect6(server.session(), host.c_str(), info.port, password,
-				    identity.nickname.c_str(),
-				    identity.username.c_str(),
-				    identity.realname.c_str());
-	} else {
-		code = irc_connect(server.session(), host.c_str(), info.port, password,
-				   identity.nickname.c_str(),
-				   identity.username.c_str(),
-				   identity.realname.c_str());
-	}
-
-	return code == 0;
-}
-
-void ServerState::prepareConnected(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-	if (!irc_is_connected(server.session())) {
-		const ServerSettings &settings = server.settings();
-
-		log::warning() << "server " << server.info().name << ": disconnected" << std::endl;
-
-		if (settings.recotimeout > 0) {
-			log::warning() << "server " << server.info().name << ": retrying in "
-					  << settings.recotimeout << " seconds" << std::endl;
-		}
-
-		server.next(ServerState::Disconnected);
-	} else {
-		irc_add_select_descriptors(server.session(), &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-	}
-}
-
-void ServerState::prepareConnecting(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-	/*
-	 * The connect function will either fail if the hostname wasn't resolved
-	 * or if any of the internal functions fail.
-	 *
-	 * It returns success if the connection was successful but it does not
-	 * mean that connection is established.
-	 *
-	 * Because this function will be called repeatidly, the connection was started and we're still not
-	 * connected in the specified timeout time, we mark the server as disconnected.
-	 *
-	 * Otherwise, the libircclient event_connect will change the state.
-	 */
-	const ServerInfo &info = server.info();
-
-	if (m_started) {
-		const ServerSettings &settings = server.settings();
-
-		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
-			log::warning() << "server " << info.name << ": timeout while connecting" << std::endl;
-			server.next(ServerState::Disconnected);
-		} else if (!irc_is_connected(server.session())) {
-			log::warning() << "server " << info.name << ": error while connecting: ";
-			log::warning() << irc_strerror(irc_errno(server.session())) << std::endl;
-
-			if (settings.recotries != 0)
-				log::warning() << "server " << info.name << ": retrying in " << settings.recotimeout << " seconds" << std::endl;
-
-			server.next(ServerState::Disconnected);
-		} else {
-			irc_add_select_descriptors(server.session(), &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-		}
-	} else {
-		/*
-		 * This is needed if irccd is started before DHCP or if
-		 * DNS cache is outdated.
-		 *
-		 * For more information see bug #190.
-		 */
-#if !defined(_WIN32)
-		(void)res_init();
-#endif
-		log::info() << "server " << info.name << ": trying to connect to " << info.host << ", port " << info.port << std::endl;
-
-		if (!connect(server)) {
-			log::warning() << "server " << info.name << ": disconnected while connecting: ";
-			log::warning() << irc_strerror(irc_errno(server.session())) << std::endl;
-			server.next(ServerState::Disconnected);
-		} else {
-			m_started = true;
-		}
-	}
-}
-
-void ServerState::prepareDisconnected(Server &server, fd_set &, fd_set &, net::Handle &)
-{
-	const ServerInfo &info = server.info();
-	ServerSettings &settings = server.settings();
-
-	if (settings.recotries == 0) {
-		log::warning() << "server " << info.name << ": reconnection disabled, skipping" << std::endl;
-		server.onDie();
-	} else if (settings.recotries > 0 && settings.recocurrent > settings.recotries) {
-		log::warning() << "server " << info.name << ": giving up" << std::endl;
-		server.onDie();
-	} else {
-		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
-			irc_disconnect(server.session());
-
-			settings.recocurrent ++;
-			server.next(ServerState::Connecting);
-		}
-	}
-}
-
-ServerState::ServerState(Type type)
-	: m_type(type)
-{
-	assert(static_cast<int>(m_type) >= static_cast<int>(ServerState::Undefined));
-	assert(static_cast<int>(m_type) <= static_cast<int>(ServerState::Disconnected));
-}
-
-void ServerState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-	switch (m_type) {
-	case Connecting:
-		prepareConnecting(server, setinput, setoutput, maxfd);
-		break;
-	case Connected:
-		prepareConnected(server, setinput, setoutput, maxfd);
-		break;
-	case Disconnected:
-		prepareDisconnected(server, setinput, setoutput, maxfd);
-		break;
-	default:
-		break;
-	}
-}
-
-} // !irccd
--- a/irccd/server-state.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * server-state.h -- server current state
- *
- * 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_SERVER_STATE_H_
-#define _IRCCD_SERVER_STATE_H_
-
-#include <irccd-config.h>
-
-#include <elapsed-timer.h>
-#include <sockets.h>
-
-namespace irccd {
-
-class Server;
-
-/**
- * @class ServerState
- * @brief Server current state.
- */
-class ServerState {
-public:
-	/**
-	 * @enum Type
-	 * @brief Server state
-	 */
-	enum Type {
-		Undefined,	//!< Not defined yet
-		Connecting,	//!< Connecting to the server
-		Connected,	//!< Connected and running
-		Disconnected,	//!< Disconnected and waiting before retrying
-	};
-
-private:
-	Type m_type;
-
-	/* For ServerState::Connecting */
-	bool m_started{false};
-	ElapsedTimer m_timer;
-
-	/* Private helpers */
-	bool connect(Server &server);
-
-	/* Different preparation */
-	void prepareConnected(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
-	void prepareConnecting(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
-	void prepareDisconnected(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
-
-public:
-	ServerState(Type type);
-
-	void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
-
-	inline Type type() const noexcept
-	{
-		return m_type;
-	}
-};
-
-} // !irccd
-
-#endif // !_IRCCD_SERVER_STATE_H_
-
--- a/irccd/server.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,448 +0,0 @@
-/*
- * server.cpp -- an IRC server
- *
- * 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 <cerrno>
-#include <cstring>
-#include <stdexcept>
-
-#include <libirc_rfcnumeric.h>
-
-#include <logger.h>
-#include <util.h>
-
-#include "server.h"
-
-#if defined(WITH_JS)
-#  include "js.h"
-#endif
-
-namespace irccd {
-
-bool Server::isSelf(const std::string &nick) const noexcept
-{
-	char target[32]{0};
-
-	irc_target_get_nick(nick.c_str(), target, sizeof (target));
-
-	return m_identity.nickname == target;
-}
-
-void Server::extractPrefixes(const std::string &line)
-{
-	std::pair<char, char> table[16];
-	std::string buf = line.substr(7);
-
-	for (int i = 0; i < 16; ++i)
-		table[i] = std::make_pair(-1, -1);
-
-	int j = 0;
-	bool readModes = true;
-	for (size_t i = 0; i < buf.size(); ++i) {
-		if (buf[i] == '(')
-			continue;
-		if (buf[i] == ')') {
-			j = 0;
-			readModes = false;
-			continue;
-		}
-
-		if (readModes)
-			table[j++].first = buf[i];
-		else
-			table[j++].second = buf[i];
-	}
-
-	// Put these as a map of mode to prefix
-	for (int i = 0; i < 16; ++i) {
-		auto key = static_cast<ServerChanMode>(table[i].first);
-		auto value = table[i].second;
-
-		m_info.modes.emplace(key, value);
-	}
-}
-
-std::string Server::cleanPrefix(std::string nickname) const noexcept
-{
-	if (nickname.length() > 0)
-		for (const auto &pair : m_info.modes)
-			if (nickname[0] == pair.second)
-				nickname.erase(0, 1);
-
-	return nickname;
-}
-
-void Server::handleConnect(const char *, const char **) noexcept
-{
-	/* Reset the number of tried reconnection. */
-	m_settings.recocurrent = 0;
-
-	/* Don't forget to change state and notify. */
-	next(ServerState::Connected);
-	onConnect();
-
-	/* Auto join listed channels. */
-	for (const ServerChannel &channel : m_settings.channels) {
-		log::info() << "server " << m_info.name << ": auto joining " << channel.name << std::endl;
-		join(channel.name, channel.password);
-	}
-}
-
-void Server::handleChannel(const char *orig, const char **params) noexcept
-{
-	onMessage(strify(orig), strify(params[0]), strify(params[1]));
-}
-
-void Server::handleChannelMode(const char *orig, const char **params) noexcept
-{
-	onChannelMode(strify(orig), strify(params[0]), strify(params[1]), strify(params[2]));
-}
-
-void Server::handleChannelNotice(const char *orig, const char **params) noexcept
-{
-	onChannelNotice(strify(orig), strify(params[0]), strify(params[1]));
-}
-
-void Server::handleCtcpAction(const char *orig, const char **params) noexcept
-{
-	onMe(strify(orig), strify(params[0]), strify(params[1]));
-}
-
-void Server::handleInvite(const char *orig, const char **params) noexcept
-{
-	/* If joininvite is set, join the channel */
-	if ((m_settings.flags & ServerSettings::JoinInvite) && isSelf(strify(params[0])))
-		join(strify(params[1]));
-
-	/*
-	 * The libircclient says that invite contains the target nickname, it's quite
-	 * uncommon to need it so it is passed as the last argument to be
-	 * optional in the plugin.
-	 */
-	onInvite(strify(orig), strify(params[1]), strify(params[0]));
-}
-
-void Server::handleJoin(const char *orig, const char **params) noexcept
-{
-	onJoin(strify(orig), strify(params[0]));
-}
-
-void Server::handleKick(const char *orig, const char **params) noexcept
-{
-	/* Rejoin the channel if the option has been set and I was kicked. */
-	if ((m_settings.flags & ServerSettings::AutoRejoin) && isSelf(strify(params[1])))
-		join(strify(params[0]));
-
-	onKick(strify(orig), strify(params[0]), strify(params[1]), strify(params[2]));
-}
-
-void Server::handleMode(const char *orig, const char **params) noexcept
-{
-	onMode(strify(orig), strify(params[1]));
-}
-
-void Server::handleNick(const char *orig, const char **params) noexcept
-{
-	/* Update our nickname. */
-	if (isSelf(strify(orig)))
-		m_identity.nickname = strify(params[0]);
-
-	onNick(strify(orig), strify(params[0]));
-}
-
-void Server::handleNotice(const char *orig, const char **params) noexcept
-{
-	/*
-	 * As for handleInvite, the notice provides the target nickname, we discard it.
-	 */
-	onNotice(strify(orig), strify(params[1]));
-}
-
-void Server::handleNumeric(unsigned int event, const char **params, unsigned int c) noexcept
-{
-	if (event == LIBIRC_RFC_RPL_NAMREPLY) {
-		/*
-		 * Called multiple times to list clients on a channel.
-		 *
-		 * params[0] == originator
-		 * params[1] == '='
-		 * params[2] == channel
-		 * params[3] == list of users with their prefixes
-		 *
-		 * IDEA for the future: maybe give the appropriate mode as a second parameter in onNames.
-		 */
-		if (c < 4 || params[2] == nullptr || params[3] == nullptr)
-			return;
-
-		std::vector<std::string> users = util::split(params[3], " \t");
-
-		/* The listing may add some prefixes, remove them if needed */
-		for (std::string u : users)
-			m_namesMap[params[2]].insert(cleanPrefix(u));
-	} else if (event == LIBIRC_RFC_RPL_ENDOFNAMES) {
-		/*
-		 * Called when end of name listing has finished on a channel.
-		 *
-		 * params[0] == originator
-		 * params[1] == channel
-		 * params[2] == End of NAMES list
-		 */
-
-		if (c < 3 || params[1] == nullptr)
-			return;
-
-		auto it = m_namesMap.find(params[1]);
-		if (it != m_namesMap.end()) {
-			onNames(params[1], it->second);
-
-			/* Don't forget to remove the list */
-			m_namesMap.erase(it);
-		}
-	} else if (event == LIBIRC_RFC_RPL_WHOISUSER) {
-		/*
-		 * Called when whois information has been partially received.
-		 *
-		 * params[0] == originator
-		 * params[1] == nickname
-		 * params[2] == username
-		 * params[3] == host
-		 * params[4] == * (no idea what is that)
-		 * params[5] == realname
-		 */
-		if (c < 6 || !params[1] || !params[2] || !params[3] || !params[5])
-			return;
-
-		ServerWhois info;
-
-		info.nick = strify(params[1]);
-		info.user = strify(params[2]);
-		info.host = strify(params[3]);
-		info.realname = strify(params[5]);
-
-		m_whoisMap.emplace(info.nick, info);
-	} else if (event == LIBIRC_RFC_RPL_WHOISCHANNELS) {
-		/*
-		 * Called when we have received channels for one user.
-		 *
-		 * params[0] == originator
-		 * params[1] == nickname
-		 * params[2] == list of channels with their prefixes
-		 */
-		if (c < 3 || !params[1] || !params[2])
-			return;
-
-		auto it = m_whoisMap.find(params[1]);
-		if (it != m_whoisMap.end()) {
-			std::vector<std::string> channels = util::split(params[2], " \t");
-
-			/* Clean their prefixes */
-			for (auto &s : channels)
-				s = cleanPrefix(s);
-
-			/* Insert */
-			it->second.channels = std::move(channels);
-		}
-	} else if (event == LIBIRC_RFC_RPL_ENDOFWHOIS) {
-		/*
-		 * Called when whois is finished.
-		 *
-		 * params[0] == originator
-		 * params[1] == nickname
-		 * params[2] == End of WHOIS list
-		 */
-
-		auto it = m_whoisMap.find(params[1]);
-		if (it != m_whoisMap.end()) {
-			onWhois(it->second);
-
-			/* Don't forget to remove */
-			m_whoisMap.erase(it);
-		}
-	} else if (event == /* RPL_BOUNCE */ 5) {
-		/*
-		 * The event 5 is usually RPL_BOUNCE, but we always see it as ISUPPORT.
-		 */
-		for (unsigned int i = 0; i < c; ++i) {
-			if (strncmp(params[i], "PREFIX", 6) == 0) {
-				extractPrefixes(params[i]);
-				break;
-			}
-		}
-	}
-}
-
-void Server::handlePart(const char *orig, const char **params) noexcept
-{
-	onPart(strify(orig), strify(params[0]), strify(params[1]));
-}
-
-void Server::handleQuery(const char *orig, const char **params) noexcept
-{
-	onQuery(strify(orig), strify(params[1]));
-}
-
-void Server::handleTopic(const char *orig, const char **params) noexcept
-{
-	onTopic(strify(orig), strify(params[0]), strify(params[1]));
-}
-
-ServerChannel Server::splitChannel(const std::string &value)
-{
-	auto pos = value.find(':');
-
-	if (pos != std::string::npos)
-		return ServerChannel{value.substr(0, pos), value.substr(pos + 1)};
-
-	return ServerChannel{value, ""};
-}
-
-Server::Server(ServerInfo info, ServerIdentity identity, ServerSettings settings)
-	: m_info(std::move(info))
-	, m_settings(std::move(settings))
-	, m_identity(std::move(identity))
-	, m_session(nullptr, nullptr)
-	, m_state(ServerState::Connecting)
-	, m_next(ServerState::Undefined)
-{
-	irc_callbacks_t callbacks;
-
-	/*
-	 * GCC 4.9.2 triggers some missing-field-initializers warnings when
-	 * using uniform initialization so use a std::memset as a workaround.
-	 */
-	std::memset(&callbacks, 0, sizeof (irc_callbacks_t));
-
-	/*
-	 * Convert the raw pointer functions from libircclient to Server member
-	 * function.
-	 *
-	 * While doing this, discard useless arguments.
-	 */
-	callbacks.event_channel = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleChannel(orig, params);
-	};
-	callbacks.event_channel_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleChannelNotice(orig, params);
-	};
-	callbacks.event_connect = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleConnect(orig, params);
-	};
-	callbacks.event_ctcp_action = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleCtcpAction(orig, params);
-	};
-	callbacks.event_invite = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleInvite(orig, params);
-	};
-	callbacks.event_join = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleJoin(orig, params);
-	};
-	callbacks.event_kick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleKick(orig, params);
-	};
-	callbacks.event_mode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleChannelMode(orig, params);
-	};
-	callbacks.event_nick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleNick(orig, params);
-	};
-	callbacks.event_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleNotice(orig, params);
-	};
-	callbacks.event_numeric = [] (irc_session_t *session, unsigned int event, const char *, const char **params, unsigned int count) {
-		static_cast<Server *>(irc_get_ctx(session))->handleNumeric(event, params, count);
-	};
-	callbacks.event_part = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handlePart(orig, params);
-	};
-	callbacks.event_privmsg = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleQuery(orig, params);
-	};
-	callbacks.event_topic = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleTopic(orig, params);
-	};
-	callbacks.event_umode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-		static_cast<Server *>(irc_get_ctx(session))->handleMode(orig, params);
-	};
-
-	m_session = Session{irc_create_session(&callbacks), irc_destroy_session};
-
-	/* Save this to the session */
-	irc_set_ctx(m_session.get(), this);
-	irc_set_ctcp_version(m_session.get(), m_identity.ctcpversion.c_str());
-}
-
-Server::~Server()
-{
-	irc_disconnect(m_session.get());
-}
-
-void Server::update() noexcept
-{
-	if (m_next.type() != ServerState::Undefined) {
-		log::debug() << "server " << m_info.name << ": switching to state ";
-
-		switch (m_next.type()) {
-		case ServerState::Connecting:
-			log::debug() << "\"Connecting\"" << std::endl;
-			break;
-		case ServerState::Connected:
-			log::debug() << "\"Connected\"" << std::endl;
-			break;
-		case ServerState::Disconnected:
-			log::debug() << "\"Disconnected\"" << std::endl;
-			break;
-		default:
-			break;
-		}
-
-		m_state = std::move(m_next);
-		m_next = ServerState::Undefined;
-	}
-}
-
-void Server::sync(fd_set &setinput, fd_set &setoutput) noexcept
-{
-	/*
-	 * 1. Send maximum of command possible if available for write */
-	/*
-	 * Break on the first failure to avoid changing the order of the
-	 * commands if any of them fails.
-	 */
-	bool done = false;
-
-	while (!m_queue.empty() && !done) {
-		if (m_queue.front()())
-			m_queue.pop();
-		else
-			done = true;
-	}
-
-	/* 2. Read data */
-	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/irccd/server.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,874 +0,0 @@
-/*
- * server.h -- an IRC server
- *
- * 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_SERVER_H_
-#define _IRCCD_SERVER_H_
-
-#include <cstdint>
-#include <functional>
-#include <map>
-#include <memory>
-#include <queue>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <libircclient.h>
-
-#include <irccd-config.h>
-
-#include <logger.h>
-#include <signals.h>
-
-#include "server-state.h"
-
-namespace irccd {
-
-namespace js {
-
-class Context;
-
-} // !js
-
-/**
- * @class ServerIdentity
- * @brief Identity to use when connecting
- */
-class ServerIdentity {
-public:
-	std::string name{"irccd"};			//!< identity name
-	std::string nickname{"irccd"};			//!< nickname to show
-	std::string username{"irccd"};			//!< username to use for connection
-	std::string realname{"IRC Client Daemon"};	//!< the full real name
-	std::string ctcpversion{"IRC Client Daemon"};	//!< the CTCP version to define
-};
-
-/**
- * @class ServerChannel
- * @brief A channel to join with an optional password
- */
-class ServerChannel {
-public:
-	std::string name;				//!< the channel to join
-	std::string password;				//!< the optional password
-};
-
-/**
- * List of channels.
- */
-using ServerChannels = std::vector<ServerChannel>;
-
-/**
- * @enum ServerChanMode
- * @brief Prefixes for nicknames
- */
-enum class ServerChanMode {
-	Creator		= 'O',			//!< Channel creator
-	HalfOperator	= 'h',			//!< Half operator
-	Operator	= 'o',			//!< Channel operator
-	Protection	= 'a',			//!< Unkillable
-	Voiced		= 'v'			//!< Voice power
-};
-
-/**
- * @class ServerWhois
- * @brief Describe a whois information
- *
- * This is provided when whois command was requested.
- */
-class ServerWhois {
-public:
-	std::string nick;			//!< user's nickname
-	std::string user;			//!< user's user
-	std::string host;			//!< hostname
-	std::string realname;			//!< realname
-	std::vector<std::string> channels;	//!< the channels where the user is
-};
-
-/**
- * @class ServerInfo
- * @brief Server information
- *
- * This class contains everything needed to connect to a server.
- */
-class ServerInfo {
-public:
-	enum {
-		Ipv6		= (1 << 0),	//!< Connect using IPv6
-		Ssl		= (1 << 1),	//!< Use SSL
-		SslVerify	= (1 << 2)	//!< Verify SSL
-	};
-
-	std::string name;			//!< Server's name
-	std::string host;			//!< Hostname
-	std::string password;			//!< Optional server password
-	std::uint16_t port{6667};		//!< Server's port
-	std::uint8_t flags{0};			//!< Optional flags
-	std::map<ServerChanMode, char> modes;	//!< IRC modes (e.g. @~)
-};
-
-/**
- * @class ServerSettings
- * @brief Contains settings to tweak the server
- *
- * This class contains additional settings that tweaks the
- * server operations.
- */
-class ServerSettings {
-public:
-	enum {
-		AutoRejoin	= (1 << 0),	//!< Auto rejoin a channel after being kicked
-		JoinInvite	= (1 << 1)	//!< Join a channel on invitation
-	};
-
-	ServerChannels channels;		//!< List of channel to join
-	std::string command{"!"};		//!< The command character to trigger plugin command
-	std::int8_t recotries{-1};		//!< Number of tries to reconnect before giving up
-	std::uint16_t recotimeout{30};		//!< Number of seconds to wait before trying to connect
-	std::uint8_t flags{0};			//!< Optional flags
-
-	/* Private */
-	std::int8_t recocurrent{1};		//!< number of tries tested
-};
-
-/**
- * Deferred command to send to the server.
- *
- * If the command returns true, it has been correctly buffered for outgoing
- * and removed from the queue.
- */
-using ServerCommand = std::function<bool ()>;
-
-/**
- * @class Server
- * @brief The class that connect to a IRC server
- *
- * The server is a class that stores callbacks which will be called on IRC events. It is the lowest part of the
- * connection to a server, it can be used directly by the user to connect to a server.
- *
- * The server has several signals that will be emitted when data has arrived.
- *
- * When adding a server to the irccd instance using Irccd::addServer, these signals are connected to generate
- * events that will be dispatched to the plugins and to the transports.
- *
- * Note: the server is set in non blocking mode, commands are placed in a queue and sent when only when they are ready.
- */
-class Server {
-public:
-	/**
-	 * Signal: onChannelMode
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone changed the channel mode.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel
-	 * - the mode
-	 * - the optional mode argument
-	 */
-	Signal<std::string, std::string, std::string, std::string> onChannelMode;
-
-	/**
-	 * Signal: onChannelNotice
-	 * ------------------------------------------------
-	 *
-	 * Triggered when a notice has been sent on a channel.
-	 *
-	 * Arguments:
-	 * - the origin (the nickname who has sent the notice)
-	 * - the channel name
-	 * - the notice message
-	 */
-	Signal<std::string, std::string, std::string> onChannelNotice;
-
-	/**
-	 * Signal: onConnect
-	 * ------------------------------------------------
-	 *
-	 * Triggered when the server is successfully connected.
-	 */
-	Signal<> onConnect;
-
-	/**
-	 * Signal: onDie
-	 * ----------------------------------------------------------
-	 *
-	 * The server is dead.
-	 */
-	Signal<> onDie;
-
-	/**
-	 * Signal: onInvite
-	 * ------------------------------------------------
-	 *
-	 * Triggered when an invite has been sent to you (the bot).
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel
-	 * - your nickname
-	 */
-	Signal<std::string, std::string, std::string> onInvite;
-
-	/**
-	 * Signal: onJoin
-	 * ------------------------------------------------
-	 *
-	 * Triggered when a user has joined the channel, it also includes you.
-	 *
-	 * Arguments:
-	 * - the origin (may be you)
-	 * - the channel
-	 */
-	Signal<std::string, std::string> onJoin;
-
-	/**
-	 * Signal: onKick
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone has been kicked from a channel.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel
-	 * - the target who has been kicked
-	 * - the optional reason
-	 */
-	Signal<std::string, std::string, std::string, std::string> onKick;
-
-	/**
-	 * ServerEvent: onMessage
-	 * ------------------------------------------------
-	 *
-	 * Triggered when a message on a channel has been sent.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel
-	 * - the message
-	 */
-	Signal<std::string, std::string, std::string> onMessage;
-
-	/**
-	 * Signal: onMe
-	 * ------------------------------------------------
-	 *
-	 * Triggered on a CTCP Action.
-	 *
-	 * This is both used in a channel and in a private message so the target
-	 * may be a channel or your nickname.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the target
-	 * - the message
-	 */
-	Signal<std::string, std::string, std::string> onMe;
-
-	/**
-	 * Signal: onMode
-	 * ------------------------------------------------
-	 *
-	 * Triggered when the server changed your user mode.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the mode (e.g +i)
-	 */
-	Signal<std::string, std::string> onMode;
-
-	/**
-	 * Signal: onNames
-	 * ------------------------------------------------
-	 *
-	 * Triggered when names listing has finished on a channel.
-	 *
-	 * Arguments:
-	 * - the channel
-	 * - the ordered list of names
-	 */
-	Signal<std::string, std::set<std::string>> onNames;
-
-	/**
-	 * Signal: onNick
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone changed its nickname, it also includes you.
-	 *
-	 * Arguments:
-	 * - the old nickname (may be you)
-	 * - the new nickname
-	 */
-	Signal<std::string, std::string> onNick;
-
-	/**
-	 * Signal: onNotice
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone has sent a notice to you.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the notice message
-	 */
-	Signal<std::string, std::string> onNotice;
-
-	/**
-	 * Signal: onPart
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone has left the channel.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel that the nickname has left
-	 * - the optional reason
-	 */
-	Signal<std::string, std::string, std::string> onPart;
-
-	/**
-	 * Signal: onQuery
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone has sent you a private message.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the message
-	 */
-	Signal<std::string, std::string> onQuery;
-
-	/**
-	 * Signal: onTopic
-	 * ------------------------------------------------
-	 *
-	 * Triggered when someone changed the channel topic.
-	 *
-	 * Arguments:
-	 * - the origin
-	 * - the channel
-	 * - the new topic
-	 */
-	Signal<std::string, std::string, std::string> onTopic;
-
-	/*
-	 * Signal: onWhois
-	 * ------------------------------------------------
-	 *
-	 * Triggered when whois information has been received.
-	 *
-	 * Arguments:
-	 * - the whois object
-	 */
-	Signal<ServerWhois> onWhois;
-
-private:
-	using Session = std::unique_ptr<irc_session_t, void (*)(irc_session_t *)>;
-	using Queue = std::queue<ServerCommand>;
-
-	/**
-	 * List of NAMES being built.
-	 */
-	using NamesMap = std::unordered_map<std::string, std::set<std::string>>;
-
-	/**
-	 * List of WHOIS being built.
-	 */
-	using WhoisMap	= std::unordered_map<std::string, ServerWhois>;
-
-private:
-	ServerInfo m_info;
-	ServerSettings m_settings;
-	ServerIdentity m_identity;
-	Session m_session;
-	ServerState m_state;
-	ServerState m_next;
-	Queue m_queue;
-
-	/*
-	 * The names map is being built by a successive call to handleNumeric so we need to store a temporary
-	 * map by channels to list of names. Then, when we receive the end of names listing, we remove the
-	 * temporary set of names and calls the appropriate signal.
-	 */
-	NamesMap m_namesMap;
-	WhoisMap m_whoisMap;
-
-	bool isSelf(const std::string &nick) const noexcept;
-	void extractPrefixes(const std::string &line);
-	std::string cleanPrefix(std::string nickname) const noexcept;
-
-	inline std::string strify(const char *s)
-	{
-		return (s == nullptr) ? "" : std::string(s);
-	}
-
-	void handleChannel(const char *, const char **) noexcept;
-	void handleChannelMode(const char *, const char **) noexcept;
-	void handleChannelNotice(const char *, const char **) noexcept;
-	void handleConnect(const char *, const char **) noexcept;
-	void handleCtcpAction(const char *, const char **) noexcept;
-	void handleInvite(const char *, const char **) noexcept;
-	void handleJoin(const char *, const char **) noexcept;
-	void handleKick(const char *, const char **) noexcept;
-	void handleMode(const char *, const char **) noexcept;
-	void handleNick(const char *, const char **) noexcept;
-	void handleNotice(const char *, const char **) noexcept;
-	void handleNumeric(unsigned int, const char **, unsigned int) noexcept;
-	void handlePart(const char *, const char **) noexcept;
-	void handleQuery(const char *, const char **) noexcept;
-	void handleTopic(const char *, const char **) noexcept;
-
-public:
-	/**
-	 * Split a channel from the form channel:password into a ServerChannel object.
-	 *
-	 * @param value the value
-	 * @return a channel
-	 */
-	static ServerChannel splitChannel(const std::string &value);
-
-	/**
-	 * Construct a server.
-	 *
-	 * @param info the information
-	 * @param identity the identity
-	 * @param settings the settings
-	 */
-	Server(ServerInfo info, ServerIdentity identity = {}, ServerSettings settings = {});
-
-	/**
-	 * Destructor. Close the connection if needed.
-	 */
-	virtual ~Server();
-
-	/**
-	 * Set the next state to be used. This function is thread safe because
-	 * the server manager may set the next state to the current state.
-	 *
-	 * If the server is installed into the ServerManager, it is called
-	 * automatically.
-	 *
-	 * @param type the new state type
-	 * @warning Not thread-safe
-	 */
-	inline void next(ServerState::Type type)
-	{
-		m_next = ServerState(type);
-	}
-
-	/**
-	 * Switch to next state if it has.
-	 *
-	 * If the server is installed into irccd, it is called automatically.
-	 *
-	 * @warning Not thread-safe
-	 */
-	void update() noexcept;
-
-	/**
-	 * Request to disconnect. This function does not notify the
-	 * ServerService.
-	 *
-	 * @see Irccd::serverDisconnect
-	 * @note Thread-safe
-	 */
-	inline void disconnect() noexcept
-	{
-		using namespace std::placeholders;
-
-		irc_disconnect(m_session.get());
-		onDie();
-	}
-
-	/**
-	 * Asks for a reconnection. This function does not notify the
-	 * ServerService.
-	 *
-	 * @see Irccd::serverReconnect
-	 * @note Thread-safe
-	 */
-	inline void reconnect() noexcept
-	{
-		irc_disconnect(m_session.get());
-		next(ServerState::Type::Connecting);
-	}
-
-	/**
-	 * Flush the pending commands if possible. This function will send
-	 * as much as possible commands.
-	 *
-	 * If the server is installed into the ServerManager, it is called
-	 * automatically.
-	 *
-	 * @note Thread-safe
-	 */
-	void flush() noexcept;
-
-	/**
-	 * Prepare the IRC Session to the socket.
-	 *
-	 * If the server is installed into the ServerManager, it is called
-	 * automatically.
-	 *
-	 * @warning Not thread-safe
-	 */
-	inline void prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept
-	{
-		m_state.prepare(*this, setinput, setoutput, maxfd);
-	}
-
-	/**
-	 * Process incoming/outgoing data after selection.
-	 *
-	 * If the server is installed into the ServerManager, it is called
-	 * automatically.
-	 *
-	 * @param setinput
-	 * @param setoutput
-	 * @throw any exception that have been throw from user functions
-	 */
-	void sync(fd_set &setinput, fd_set &setoutput) noexcept;
-
-	/**
-	 * Get the server information.
-	 *
-	 * @warning This overload should not be used by the user, it is required to
-	 *          update the nickname.
-	 * @return the server information
-	 */
-	inline ServerInfo &info() noexcept
-	{
-		return m_info;
-	}
-
-	/**
-	 * Get the server information.
-	 *
-	 * @return the server information
-	 */
-	inline const ServerInfo &info() const noexcept
-	{
-		return m_info;
-	}
-
-	/**
-	 * Get the server settings.
-	 *
-	 * @warning This overload should not be used by the user, it is required to
-	 *          update the reconnection information.
-	 * @return the settings
-	 */
-	inline ServerSettings &settings() noexcept
-	{
-		return m_settings;
-	}
-
-	/**
-	 * Get the server settings.
-	 *
-	 * @return the settings
-	 */
-	inline const ServerSettings &settings() const noexcept
-	{
-		return m_settings;
-	}
-
-	/**
-	 * Get the identity.
-	 *
-	 * @return the identity
-	 */
-	inline ServerIdentity &identity() noexcept
-	{
-		return m_identity;
-	}
-
-	/**
-	 * Overloaded function
-	 *
-	 * @return the identity
-	 */
-	inline const ServerIdentity &identity() const noexcept
-	{
-		return m_identity;
-	}
-
-	/**
-	 * Get the current state identifier. Should not be used by user code.
-	 *
-	 * @note Thread-safe but the state may change just after the call
-	 */
-	inline ServerState::Type type() const noexcept
-	{
-		return m_state.type();
-	}
-
-	/**
-	 * Get the libircclient session.
-	 *
-	 * @warning Do not use this function, it is only required for ServerState's
-	 * @return the session
-	 */
-	inline irc_session_t *session() noexcept
-	{
-		return m_session.get();
-	}
-
-	/**
-	 * Change the channel mode.
-	 *
-	 * @param channel the channel
-	 * @param mode the new mode
-	 * @note Thread-safe
-	 */
-	inline void cmode(std::string channel, std::string mode)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_channel_mode(m_session.get(), channel.c_str(), mode.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Send a channel notice.
-	 *
-	 * @param channel the channel
-	 * @param message message notice
-	 * @note Thread-safe
-	 */
-	inline void cnotice(std::string channel, std::string message) noexcept
-	{
-		m_queue.push([=] () {
-			return irc_cmd_notice(this->m_session.get(), channel.c_str(), message.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Invite a user to a channel.
-	 *
-	 * @param target the target nickname
-	 * @param channel the channel
-	 * @note Thread-safe
-	 */
-	inline void invite(std::string target, std::string channel) noexcept
-	{
-		m_queue.push([=] () {
-			return irc_cmd_invite(this->m_session.get(), target.c_str(), channel.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Join a channel, the password is optional and can be kept empty.
-	 *
-	 * @param channel the channel to join
-	 * @param password the optional password
-	 * @note Thread-safe
-	 */
-	inline void join(std::string channel, std::string password = "") noexcept
-	{
-		m_queue.push([=] () {
-			const char *ptr = password.empty() ? nullptr : password.c_str();
-
-			return irc_cmd_join(this->m_session.get(), channel.c_str(), ptr) == 0;
-		});
-	}
-
-	/**
-	 * Kick someone from the channel. Please be sure to have the rights
-	 * on that channel because errors won't be reported.
-	 *
-	 * @param target the target to kick
-	 * @param channel from which channel
-	 * @param reason the optional reason
-	 * @note Thread-safe
-	 */
-	inline void kick(std::string target, std::string channel, std::string reason = "") noexcept
-	{
-		m_queue.push([=] () {
-			return irc_cmd_kick(this->m_session.get(), target.c_str(), channel.c_str(), reason.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Send a CTCP Action as known as /me. The target may be either a
-	 * channel or a nickname.
-	 *
-	 * @param target the nickname or the channel
-	 * @param message the message
-	 * @note Thread-safe
-	 */
-	inline void me(std::string target, std::string message)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_me(m_session.get(), target.c_str(), message.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Send a message to the specified target or channel.
-	 *
-	 * @param target the target
-	 * @param message the message
-	 * @note Thread-safe
-	 */
-	inline void message(std::string target, std::string message)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_msg(m_session.get(), target.c_str(), message.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Change your user mode.
-	 *
-	 * @param mode the mode
-	 * @note Thread-safe
-	 */
-	inline void mode(std::string mode)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_user_mode(m_session.get(), mode.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Request the list of names.
-	 *
-	 * @param channel the channel
-	 * @note Thread-safe
-	 */
-	inline void names(std::string channel)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_names(m_session.get(), channel.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Change your nickname.
-	 *
-	 * @param newnick the new nickname to use
-	 * @note Thread-safe
-	 */
-	inline void nick(std::string newnick)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_nick(m_session.get(), newnick.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Send a private notice.
-	 *
-	 * @param target the target
-	 * @param message the notice message
-	 * @note Thread-safe
-	 */
-	inline void notice(std::string target, std::string message)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_notice(m_session.get(), target.c_str(), message.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Part from a channel.
-	 *
-	 * Please note that the reason is not supported on all servers so if you want portability, don't provide it.
-	 *
-	 * @param channel the channel to leave
-	 * @param reason the optional reason
-	 * @note Thread-safe
-	 */
-	inline void part(std::string channel, std::string reason = "")
-	{
-		m_queue.push([=] () -> bool {
-			if (reason.empty())
-				return irc_cmd_part(m_session.get(), channel.c_str()) == 0;
-
-			return irc_send_raw(m_session.get(), "PART %s :%s", channel.c_str(), reason.c_str());
-		});
-	}
-
-	/**
-	 * Send a raw message to the IRC server. You don't need to add
-	 * message terminators.
-	 *
-	 * @warning Use this function with care
-	 * @param raw the raw message (without \r\n\r\n)
-	 * @note Thread-safe
-	 */
-	inline void send(std::string raw)
-	{
-		m_queue.push([=] () {
-			return irc_send_raw(m_session.get(), raw.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Change the channel topic.
-	 *
-	 * @param channel the channel
-	 * @param topic the desired topic
-	 * @note Thread-safe
-	 */
-	inline void topic(std::string channel, std::string topic)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_topic(m_session.get(), channel.c_str(), topic.c_str()) == 0;
-		});
-	}
-
-	/**
-	 * Request for whois information.
-	 *
-	 * @param target the target nickname
-	 * @note Thread-safe
-	 */
-	inline void whois(std::string target)
-	{
-		m_queue.push([=] () {
-			return irc_cmd_whois(this->m_session.get(), target.c_str()) == 0;
-		});
-	}
-
-#if defined(WITH_JS)
-	/**
-	 * Get the object signature.
-	 *
-	 * @return the signature
-	 */
-	static inline const char *name() noexcept
-	{
-		return "\xff""\xff""Server";
-	}
-
-	/**
-	 * Push the JavaScript prototype for a Server.
-	 *
-	 * @param ctx the context
-	 */
-	void prototype(js::Context &ctx);
-#endif
-};
-
-} // !irccd
-
-#endif // !_IRCCD_SERVER_H_
--- a/irccd/timer.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * timer.cpp -- threaded timers
- *
- * 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 <chrono>
-
-#include "timer.h"
-
-namespace irccd {
-
-void Timer::run()
-{
-	while (m_state != Stopped) {
-		std::unique_lock<std::mutex> lock(m_mutex);
-
-		/* Wait in case the timer is paused */
-		m_condition.wait(lock, [&] () {
-			return m_state != Paused;
-		});
-
-		if (m_state != Running)
-			continue;
-
-		/* Wait the timer delay or the interrupt */
-		m_condition.wait_for(lock, std::chrono::milliseconds(m_delay), [&] () {
-			return m_state != Running;
-		});
-
-		if (m_state == Running) {
-			/* Signal process */
-			onSignal();
-
-			if (m_type == TimerType::Single)
-				m_state = Stopped;
-		}
-	}
-
-	onEnd();
-}
-
-Timer::Timer(TimerType type, unsigned delay) noexcept
-	: m_type(type)
-	, m_delay(delay)
-	, m_thread([this] () { run(); })
-{
-}
-
-Timer::~Timer()
-{
-	assert(m_state != Running);
-
-	m_state = Stopped;
-	m_condition.notify_one();
-	m_thread.join();
-}
-
-void Timer::start()
-{
-	assert(m_state != Running);
-
-	m_state = Running;
-	m_condition.notify_one();
-}
-
-void Timer::stop()
-{
-	m_state = Paused;
-	m_condition.notify_one();
-}
-
-} // !irccd
--- a/irccd/timer.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/*
- * timer.h -- threaded timers
- *
- * 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_TIMER_H_
-#define _IRCCD_TIMER_H_
-
-/**
- * @file Timer.h
- * @brief Provides interval based timers for JavaScript
- */
-
-#include <atomic>
-#include <condition_variable>
-#include <functional>
-#include <mutex>
-#include <thread>
-
-#include <signals.h>
-
-namespace irccd {
-
-/**
- * @enum TimerType
- * @brief Type of timer
- */
-enum class TimerType {
-	Single,			//!< The timer ends after execution
-	Repeat			//!< The timer loops
-};
-
-/**
- * @class Timer
- * @brief Timer class
- *
- * A timer is a thread object that emits a signal periodically or just one time. It is perfectly pausable and resumable
- * to reuse the same object.
- *
- * The delay is configured in milliseconds and the user has choice to use any
- * delay needed.
- *
- * We use a condition variable to wait for the specified delay unless the timer
- * must be stopped.
- */
-class Timer {
-public:
-	/**
-	 * Signal: onSignal
-	 * ----------------------------------------------------------
-	 *
-	 * Called when the timeout expires.
-	 */
-	Signal<> onSignal;
-
-	/**
-	 * Signal: onEnd
-	 * ----------------------------------------------------------
-	 *
-	 * Called when the timeout ends.
-	 */
-	Signal<> onEnd;
-
-private:
-	enum {
-		Paused,
-		Running,
-		Stopped
-	};
-
-	TimerType m_type;
-	unsigned m_delay;
-
-	/* Thread management */
-	std::atomic<int> m_state{Paused};
-	std::mutex m_mutex;
-	std::condition_variable m_condition;
-	std::thread m_thread;
-
-	void run();
-
-public:
-	/**
-	 * Timer constructor.
-	 *
-	 * The timer is not started, use start().
-	 *
-	 * @param type the timer type
-	 * @param delay the delay in milliseconds
-	 * @post isRunning() returns false
-	 */
-	Timer(TimerType type, unsigned delay) noexcept;
-
-	/**
-	 * Destructor, closes the thread.
-	 *
-	 * @pre stop() must have been called.
-	 */
-	virtual ~Timer();
-
-	/**
-	 * Start the thread.
-	 *
-	 * @pre isRunning() must return false
-	 * @pre onSignal() must have been called
-	 * @pre onEnd() must have been called
-	 * @note Thread-safe
-	 */
-	void start();
-
-	/**
-	 * Stop the timer, may be used by the user to stop it.
-	 *
-	 * @pre isRunning() must return true
-	 * @note Thread-safe
-	 */
-	void stop();
-
-	/**
-	 * Get the type of timer.
-	 *
-	 * @return the type.
-	 */
-	inline TimerType type() const noexcept
-	{
-		return m_type;
-	}
-
-	/**
-	 * Tells if the timer has still a running thread.
-	 *
-	 * @return true if still alive
-	 * @note Thread-safe
-	 */
-	inline bool isRunning() const noexcept
-	{
-		return m_state == Running;
-	}
-};
-
-} // !irccd
-
-#endif // !_IRCCD_TIMER_H_
--- a/irccd/transport-client.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * transport-client.cpp -- client connected 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 <json.h>
-#include <logger.h>
-
-#include "transport-client.h"
-
-namespace irccd {
-
-void TransportClient::parse(const std::string &message)
-{
-	try {
-		json::Value document(json::Buffer{message});
-
-		if (!document.isObject())
-			throw std::invalid_argument("the message is not a valid JSON object");
-
-		onCommand(document);
-	} catch (const std::exception &ex) {
-		// TODO: send general error to the client
-		log::warning() << "transport: " << ex.what() << std::endl;
-	}
-}
-
-void TransportClient::sync(fd_set &setinput, fd_set &setoutput)
-{
-	if (FD_ISSET(handle(), &setinput)) {
-		log::debug() << "transport: receiving to input buffer" << std::endl;
-		receive();
-	}
-	if (FD_ISSET(handle(), &setoutput)) {
-		log::debug() << "transport: sending outgoing buffer" << std::endl;
-		send();
-	}
-}
-
-void TransportClient::ok(const std::string &command)
-{
-	m_output += "{";
-	m_output += "\"response\":\"" + command + "\",";
-	m_output += "\"status\":\"ok\"";
-	m_output += "}\r\n\r\n";
-}
-
-void TransportClient::error(const std::string &command, std::string message)
-{
-	m_output += "{";
-	m_output += "\"response\":\"" + command + "\",";
-	m_output += "\"status\":\"error\",";
-	m_output += "\"error\": \"" + json::escape(message) + "\"";
-	m_output += "}\r\n\r\n";
-}
-
-void TransportClient::send(std::string message)
-{
-	m_output += message;
-	m_output += "\r\n\r\n";
-}
-
-} // !irccd
--- a/irccd/transport-client.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-/*
- * transport-client.h -- client connected 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_TRANSPORT_CLIENT_H_
-#define _IRCCD_TRANSPORT_CLIENT_H_
-
-/**
- * @file TransportClient.h
- * @brief Client connected to irccd
- */
-
-#include <functional>
-#include <memory>
-#include <stdexcept>
-#include <string>
-
-#include <signals.h>
-#include <sockets.h>
-
-#include "server.h"
-
-namespace irccd {
-
-namespace json {
-
-class Value;
-
-} // !json
-
-/**
- * @class TransportClient
- * @brief Client connected to irccd.
- *
- * This class emits a warning upon clients request through onCommand signal.
- */
-class TransportClient {
-public:
-	/**
-	 * Signal: onCommand
-	 * ----------------------------------------------------------
-	 *
-	 * Arguments:
-	 *   - the command
-	 */
-	Signal<const json::Value &> onCommand;
-
-	/**
-	 * Signal: onDie
-	 * ----------------------------------------------------------
-	 *
-	 * The client has disconnected.
-	 */
-	Signal<> onDie;
-
-protected:
-	std::string m_input;
-	std::string m_output;
-
-	/* Parse input buffer */
-	void parse(const std::string &);
-
-	/* Do I/O */
-	virtual void receive() = 0;
-	virtual void send() = 0;
-
-public:
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~TransportClient() = default;
-
-	/**
-	 * Send or receive data, called after a select.
-	 *
-	 * @param setinput the input fd_set
-	 * @param setoutput the output fd_set
-	 */
-	void sync(fd_set &setinput, fd_set &setoutput);
-
-	/**
-	 * Notify the client that the command succeeded.
-	 *
-	 * @param command the command name
-	 */
-	void ok(const std::string &command);
-
-	/**
-	 * Send an error message to the client.
-	 *
-	 * @param command the command name
-	 * @param message the error message
-	 */
-	void error(const std::string &command, std::string message);
-
-	/**
-	 * Send some data, it will be pushed to the outgoing buffer.
-	 *
-	 * This function appends "\r\n\r\n" after the message so you don't have
-	 * to do it manually.
-	 *
-	 * @param message the message
-	 */
-	void send(std::string message);
-
-	/**
-	 * Tell if the client has data pending for output.
-	 *
-	 * @return true if has pending data to write
-	 */
-	inline bool hasOutput() const noexcept
-	{
-		return !m_output.empty();
-	}
-
-	/**
-	 * Get the underlying socket handle.
-	 *
-	 * @return the socket
-	 */
-	virtual net::Handle handle() noexcept = 0;
-};
-
-/**
- * @class TransportClient
- * @brief Template class for Tcp and Ssl sockets
- */
-template <typename Address>
-class TransportClientBase : public TransportClient {
-private:
-	net::SocketTcp<Address> m_socket;
-
-protected:
-	void send() override;
-	void receive() override;
-
-public:
-	/**
-	 * Create a client.
-	 *
-	 * @param sock the socket
-	 */
-	inline TransportClientBase(net::SocketTcp<Address> socket)
-		: m_socket(std::move(socket))
-	{
-	}
-
-	/**
-	 * @copydoc TransportClient::handle
-	 */
-	net::Handle handle() noexcept override
-	{
-		return m_socket.handle();
-	}
-};
-
-template <typename Address>
-void TransportClientBase<Address>::receive()
-{
-	try {
-		auto message = m_socket.recv(512);
-
-		if (message.empty())
-			onDie();
-
-		m_input += message;
-	} catch (const std::exception &) {
-		onDie();
-	}
-
-	std::string::size_type pos;
-	while ((pos = m_input.find("\r\n\r\n")) != std::string::npos) {
-		/*
-		 * Make a copy and erase it in case that onComplete function
-		 * throws.
-		 */
-		auto message = m_input.substr(0, pos);
-
-		m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
-
-		parse(message);
-	}
-}
-
-template <typename Address>
-void TransportClientBase<Address>::send()
-{
-	m_output.erase(0, m_socket.send(m_output));
-}
-
-} // !irccd
-
-#endif // !_IRCCD_TRANSPORT_CLIENT_H_
--- a/irccd/transport-command.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * transport-command.h -- command to execute server side
- *
- * 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_TRANSPORT_COMMAND_H_
-#define _IRCCD_TRANSPORT_COMMAND_H_
-
-#include <json.h>
-
-#include "irccd.h"
-#include "transport-client.h"
-
-namespace irccd {
-
-class Irccd;
-class TransportClient;
-
-namespace json {
-
-class Value;
-
-} // !json
-
-/**
- * @class TransportCommand
- * @brief Command to execute server side.
- */
-class TransportCommand {
-public:
-	/**
-	 * Default constructor.
-	 */
-	TransportCommand() = default;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~TransportCommand() = default;
-
-	/**
-	 * Execute the client request, the implementation can throw anything.
-	 *
-	 * @param irccd the irccd instance
-	 * @param tc the client
-	 * @param object the request
-	 */
-	virtual void exec(Irccd &irccd, TransportClient &tc, const json::Value &object) const = 0;
-};
-
-} // !irccd
-
-#endif // _IRCCD_TRANSPORT_COMMAND_H_
--- a/irccd/transport-server.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
- * transport-server.cpp -- I/O for irccd clients (acceptors)
- *
- * 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.
- */
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-#  include <cstdio>
-#endif
-
-#include <sstream>
-
-#include "transport-server.h"
-
-namespace irccd {
-
-/*
- * TransportServerIp
- * ------------------------------------------------------------------
- */
-
-TransportServerIp::TransportServerIp(int domain, const std::string &address, int port, bool ipv6only)
-	: m_socket(domain, SOCK_STREAM, 0)
-{
-	m_socket.set(net::option::SockReuseAddress{true});
-
-	/* Disable or enable IPv4 when using IPv6 */
-	if (domain == AF_INET6)
-		m_socket.set(net::option::Ipv6Only{ipv6only});
-
-	m_socket.bind(net::address::Ip{address, port, static_cast<net::address::Ip::Type>(domain)});
-	m_socket.listen();
-
-	log::info() << "transport: listening on " << address << ", port " << port << std::endl;
-}
-
-net::Handle TransportServerIp::handle() noexcept
-{
-	return m_socket.handle();
-}
-
-std::shared_ptr<TransportClient> TransportServerIp::accept()
-{
-	return std::make_shared<TransportClientBase<net::address::Ip>>(m_socket.accept(nullptr));
-}
-
-/*
- * TransportServerUnix
- * ------------------------------------------------------------------
- */
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-
-TransportServerUnix::TransportServerUnix(std::string path)
-	: m_path(std::move(path))
-{
-	m_socket.bind(net::address::Local{m_path, true});
-	m_socket.listen();
-
-	log::info() << "transport: listening on " << m_path << std::endl;
-}
-
-TransportServerUnix::~TransportServerUnix()
-{
-	::remove(m_path.c_str());
-}
-
-net::Handle TransportServerUnix::handle() noexcept
-{
-	return m_socket.handle();
-}
-
-std::shared_ptr<TransportClient> TransportServerUnix::accept()
-{
-	return std::make_shared<TransportClientBase<net::address::Local>>(m_socket.accept(nullptr));
-}
-
-#endif
-
-} // !irccd
--- a/irccd/transport-server.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-/*
- * transport-server.h -- I/O for irccd clients (acceptors)
- *
- * 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_TRANSPORT_SERVER_H_
-#define _IRCCD_TRANSPORT_SERVER_H_
-
-/**
- * @file transport-server.h
- * @brief Transports for irccd
- */
-
-#include <memory>
-#include <string>
-
-#include <irccd-config.h>
-
-#include <sockets.h>
-
-#include "transport-client.h"
-
-namespace irccd {
-
-/**
- * @class TransportServer
- * @brief Bring networking between irccd and irccdctl
- *
- * This class contains a master sockets for listening to TCP connections, it is then processed by irccd.
- *
- * The transport class supports the following domains:
- *
- * | Domain                | Class                 |
- * |-----------------------|-----------------------|
- * | IPv4, IPv6            | TransportServerIp     |
- * | Unix (not on Windows) | TransportServerUnix   |
- *
- * Note: IPv4 and IPv6 can be combined, using TransportServer::IPv6 and its option.
- */
-class TransportServer {
-private:
-	TransportServer(const TransportServer &) = delete;
-	TransportServer(TransportServer &&) = delete;
-
-	TransportServer &operator=(const TransportServer &) = delete;
-	TransportServer &operator=(TransportServer &&) = delete;
-
-public:
-	/**
-	 * Default constructor.
-	 */
-	TransportServer() = default;
-
-	/**
-	 * Destructor defaulted.
-	 */
-	virtual ~TransportServer() = default;
-
-	/**
-	 * Retrieve the underlying socket handle.
-	 *
-	 * @return the socket
-	 */
-	virtual net::Handle handle() noexcept = 0;
-
-	/**
-	 * Accept a new client depending on the domain.
-	 *
-	 * @return the new client
-	 */
-	virtual std::shared_ptr<TransportClient> accept() = 0;
-};
-
-/**
- * @class TransportServerIp
- * @brief Base class for both IPv4 and IPv6 servers.
- */
-class TransportServerIp : public TransportServer {
-protected:
-	net::SocketTcp<net::address::Ip> m_socket;
-
-public:
-	/**
-	 * Create a IP transport, use IPv6 or IPv4 address.
-	 *
-	 * @param domain AF_INET or AF_INET6
-	 * @param address the address or "*" for any
-	 * @param port the port number
-	 * @param ipv6only set to true to disable IPv4
-	 * @throw net::Error on failures
-	 */
-	TransportServerIp(int domain, const std::string &address, int port, bool ipv6only = true);
-
-	/**
-	 * @copydoc TransportServer::socket
-	 */
-	net::Handle handle() noexcept override;
-
-	/**
-	 * @copydoc TransportServer::accept
-	 */
-	std::shared_ptr<TransportClient> accept() override;
-};
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-
-/**
- * @class TransportServerUnix
- * @brief Implementation of transports for Unix sockets.
- */
-class TransportServerUnix : public TransportServer {
-private:
-	net::SocketTcp<net::address::Local> m_socket;
-	std::string m_path;
-
-public:
-	/**
-	 * Create a Unix transport.
-	 *
-	 * @param path the path
-	 */
-	TransportServerUnix(std::string path);
-
-	/**
-	 * Destroy the transport and remove the file.
-	 */
-	~TransportServerUnix();
-
-	/**
-	 * @copydoc TransportServer::socket
-	 */
-	net::Handle handle() noexcept override;
-
-	/**
-	 * @copydoc TransportServer::accept
-	 */
-	std::shared_ptr<TransportClient> accept() override;
-};
-
-#endif // !_WIN32
-
-} // !irccd
-
-#endif // !_IRCCD_TRANSPORT_SERVER_H_
--- a/irccdctl/CMakeLists.txt	Mon Mar 14 11:24:25 2016 +0100
+++ b/irccdctl/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -21,63 +21,6 @@
 irccd_define_executable(
 	TARGET irccdctl
 	INSTALL
-	SOURCES
-		CMakeLists.txt
-		alias.cpp
-		alias.h
-		command.h
-		command-help.cpp
-		command-help.h
-		command-plugin-info.cpp
-		command-plugin-info.h
-		command-plugin-list.cpp
-		command-plugin-list.h
-		command-plugin-load.cpp
-		command-plugin-load.h
-		command-plugin-reload.cpp
-		command-plugin-reload.h
-		command-plugin-unload.cpp
-		command-plugin-unload.h
-		command-server-cmode.cpp
-		command-server-cmode.h
-		command-server-cnotice.cpp
-		command-server-cnotice.h
-		command-server-connect.cpp
-		command-server-connect.h
-		command-server-disconnect.cpp
-		command-server-disconnect.h
-		command-server-info.cpp
-		command-server-info.h
-		command-server-invite.cpp
-		command-server-invite.h
-		command-server-join.cpp
-		command-server-join.h
-		command-server-kick.cpp
-		command-server-kick.h
-		command-server-list.cpp
-		command-server-list.h
-		command-server-me.cpp
-		command-server-me.h
-		command-server-message.cpp
-		command-server-message.h
-		command-server-mode.cpp
-		command-server-mode.h
-		command-server-nick.cpp
-		command-server-nick.h
-		command-server-notice.cpp
-		command-server-notice.h
-		command-server-part.cpp
-		command-server-part.h
-		command-server-reconnect.cpp
-		command-server-reconnect.h
-		command-server-topic.cpp
-		command-server-topic.h
-		command-watch.cpp
-		command-watch.h
-		connection.cpp
-		connection.h
-		irccdctl.cpp
-		irccdctl.h
-		main.cpp
-	LIBRARIES common
-)
\ No newline at end of file
+	SOURCES CMakeLists.txt main.cpp
+	LIBRARIES libirccd
+)
--- a/irccdctl/alias.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * alias.cpp -- create irccdctl aliases
- *
- * 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 <regex>
-
-#include "alias.h"
-
-namespace irccd {
-
-AliasArg::AliasArg(std::string value)
-{
-	assert(!value.empty());
-
-	if ((m_isPlaceholder = std::regex_match(value, std::regex("^%\\d+$"))))
-		m_value = value.substr(1);
-	else
-		m_value = std::move(value);
-}
-
-unsigned AliasArg::index() const noexcept
-{
-	assert(isPlaceholder());
-
-	return std::stoi(m_value);
-}
-
-const std::string &AliasArg::value() const noexcept
-{
-	assert(!isPlaceholder());
-
-	return m_value;
-}
-
-std::ostream &operator<<(std::ostream &out, const AliasArg &arg)
-{
-	if (arg.m_isPlaceholder)
-		out << "%" << arg.m_value;
-	else
-		out << arg.m_value;
-
-	return out;
-}
-
-} // !irccd
--- a/irccdctl/alias.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-/*
- * alias.h -- create irccdctl aliases
- *
- * 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_ALIAS_H_
-#define _IRCCDCTL_ALIAS_H_
-
-#include <ostream>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * @class AliasArg
- * @brief Describe an alias argument.
- *
- * When the user specify arguments, it can precise an applied argument or a placeholder that will be substituted
- * during command line invocation.
- *
- * Placeholders are placed using %n where n is an integer starting from 0.
- */
-class AliasArg {
-private:
-	std::string m_value;
-	bool m_isPlaceholder;
-
-public:
-	/**
-	 * Construct an argument.
-	 *
-	 * @pre value must not be empty
-	 * @param value the value
-	 */
-	AliasArg(std::string value);
-
-	/**
-	 * Check if the argument is a placeholder.
-	 *
-	 * @return true if the argument is a placeholder
-	 */
-	inline bool isPlaceholder() const noexcept
-	{
-		return m_isPlaceholder;
-	}
-
-	/**
-	 * Get the placeholder index (e.g %0 returns 0)
-	 *
-	 * @pre isPlaceholder() must return true
-	 * @return the position
-	 */
-	unsigned index() const noexcept;
-
-	/**
-	 * Get the real value.
-	 *
-	 * @pre isPlaceholder() must return false
-	 * @return the value
-	 */
-	const std::string &value() const noexcept;
-
-	friend std::ostream &operator<<(std::ostream &out, const AliasArg &);
-};
-
-/**
- * @class AliasCommand
- * @brief Describe a user-provided alias command.
- *
- * An alias command is just a command with a set of applied or placeholders arguments.
- */
-class AliasCommand {
-private:
-	std::string m_command;
-	std::vector<AliasArg> m_args;
-
-public:
-	/**
-	 * Create an alias command.
-	 *
-	 * @param command the command
-	 * @param args the arguments
-	 */
-	inline AliasCommand(std::string command, std::vector<AliasArg> args = {}) noexcept
-		: m_command(std::move(command))
-		, m_args(std::move(args))
-	{
-	}
-
-	/**
-	 * Get the command to execute.
-	 *
-	 * @return the command name
-	 */
-	inline const std::string &command() const noexcept
-	{
-		return m_command;
-	}
-
-	/**
-	 * Get the arguments.
-	 *
-	 * @return the arguments
-	 */
-	inline const std::vector<AliasArg> &args() const noexcept
-	{
-		return m_args;
-	}
-};
-
-/**
- * @class Alias
- * @brief A set of commands to execute with their arguments.
- *
- * An alias is a composition of AliasCommand, typically, the user is able to set an alias that execute a list of
- * specified commands in order they are defined.
- */
-class Alias : public std::vector<AliasCommand> {
-private:
-	std::string m_name;
-
-public:
-	/**
-	 * Create an alias.
-	 *
-	 * @param name the alias name
-	 */
-	inline Alias(std::string name) noexcept
-		: m_name(std::move(name))
-	{
-	}
-
-	/**
-	 * Get the alias name.
-	 *
-	 * @return the name
-	 */
-	inline const std::string &name() const noexcept
-	{
-		return m_name;
-	}
-};
-
-} // !irccd
-
-#endif // !_IRCCDCTL_ALIAS_H_
--- a/irccdctl/command-help.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +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 "command-help.h"
-
-namespace irccd {
-
-namespace command {
-
-void Help::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " help topic\n\n";
-	log::warning() << "Show command help\n\n";
-	log::warning() << sys::programName() << " help server-message" << std::endl;
-}
-
-void Help::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("help requires 1 argument");
-
-	auto it = ctl.commands().find(args[0]);
-
-	if (it == ctl.commands().end())
-		throw std::invalid_argument(": there is no topic named '" + args[0] + "'");
-
-	it->second->usage(ctl);
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-help.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +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 Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- a/irccdctl/command-plugin-info.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * command-plugin-info.cpp -- implementation of irccdctl plugin-info
- *
- * 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 "command-plugin-info.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginInfo::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " plugin-info name\n\n";
-	log::warning() << "Get plugin information.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t " << sys::programName() << " plugin-info ask" << std::endl;
-}
-
-void PluginInfo::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("plugin-info requires 1 argument");
-
-	ctl.connection().send(json::object({
-		{ "command",	"plugin-info"	},
-		{ "plugin",	args[0]		}
-	}).toJson(0));
-
-	json::Value obj = ctl.connection().next("plugin-info");
-
-	if (obj.contains("error"))
-		throw std::runtime_error(obj.at("error").toString());
-
-	/* Plugin information */
-	std::cout << std::boolalpha;
-	std::cout << "Author         : " << obj.valueOr("author", "").toString(true) << std::endl;
-	std::cout << "License        : " << obj.valueOr("license", "").toString(true) << std::endl;
-	std::cout << "Summary        : " << obj.valueOr("summary", "").toString(true) << std::endl;
-	std::cout << "Version        : " << obj.valueOr("version", "").toString(true) << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-plugin-info.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-plugin-info.h -- implementation of irccdctl plugin-info
- *
- * 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_PLUGIN_INFO_H_
-#define _IRCCDCTL_COMMAND_PLUGIN_INFO_H_
-
-/**
- * @file command-plugin-info.h
- * @brief Implementation of irccdctl plugin-info.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginInfo
- * @brief Implementation of irccdctl plugin-info.
- */
-class PluginInfo : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !commands
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_PLUGIN_INFO_H_
--- a/irccdctl/command-plugin-list.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * command-plugin-list.cpp -- implementation of irccdctl plugin-list
- *
- * 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 "command-plugin-list.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginList::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " plugin-list\n\n";
-	log::warning() << "Get the list of all loaded plugins.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " plugin-list" << std::endl;
-}
-
-void PluginList::exec(Irccdctl &ctl, const std::vector<std::string> &) const
-{
-	ctl.connection().send(json::object({
-		{ "command", "plugin-list" }
-	}).toJson(0));
-
-	json::Value object = ctl.connection().next("plugin-list");
-	json::Value list = object.valueOr("list", json::Type::Array, json::array({}));
-
-	for (const auto &n : list)
-		std::cout << n.toString() << std::endl;
-}
-
-} // !command
-
-} // !irccd
-
--- a/irccdctl/command-plugin-list.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-plugin-list.h -- implementation of irccdctl plugin-list
- *
- * 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_PLUGIN_LIST_H_
-#define _IRCCDCTL_COMMAND_PLUGIN_LIST_H_
-
-/**
- * @file command-plugin-list.h
- * @brief Implementation of irccdctl plugin-list.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginList
- * @brief Implementation of irccdctl plugin-list.
- */
-class PluginList : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_PLUGIN_LIST_H_
--- a/irccdctl/command-plugin-load.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * command-plugin-load.cpp -- implementation of irccdctl plugin-load
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-plugin-load.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginLoad::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " plugin-load name\n\n";
-	log::warning() << "Load a plugin into the irccd instance.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t " << sys::programName() << " plugin-load logger" << std::endl;
-}
-
-void PluginLoad::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("plugin-load requires 1 argument");
-
-	ctl.connection().send(json::object({
-		{ "command",	"plugin-load"	},
-		{ "plugin",	args[0]		}
-	}).toJson(0));
-
-	ctl.connection().verify("plugin-load");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-plugin-load.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-plugin-load.h -- implementation of irccdctl plugin-load
- *
- * 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_PLUGIN_LOAD_H_
-#define _IRCCDCTL_COMMAND_PLUGIN_LOAD_H_
-
-/**
- * @file command-plugin-load.h
- * @brief Implementation of irccdctl plugin-load.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginLoad
- * @brief Implementation of irccdctl plugin-load.
- */
-class PluginLoad : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_PLUGIN_LOAD_H_
--- a/irccdctl/command-plugin-reload.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-plugin-reload.cpp -- implementation of irccdctl plugin-reload
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-plugin-reload.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginReload::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " plugin-reload name\n\n";
-	log::warning() << "Reload a plugin by calling the appropriate onReload event, the plugin is not\n";
-	log::warning() << "unloaded and must be already loaded.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " plugin-reload logger" << std::endl;
-}
-
-void PluginReload::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("plugin-reload requires 1 argument");
-
-	ctl.connection().send(json::object({
-		{ "command",	"plugin-reload"	},
-		{ "plugin",	args[0]		}
-	}).toJson(0));
-
-	ctl.connection().verify("plugin-reload");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-plugin-reload.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-plugin-reload.h -- implementation of irccdctl plugin-reload
- *
- * 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_PLUGIN_RELOAD_H_
-#define _IRCCDCTL_COMMAND_PLUGIN_RELOAD_H_
-
-/**
- * @file command-plugin-reload.h
- * @brief Implementation of irccdctl plugin-reload.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginReload
- * @brief Implementation of irccdctl plugin-reload.
- */
-class PluginReload : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_PLUGIN_RELOAD_H_
--- a/irccdctl/command-plugin-unload.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * command-plugin-unload.cpp -- implementation of irccdctl plugin-unload
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-plugin-unload.h"
-
-namespace irccd {
-
-namespace command {
-
-void PluginUnload::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " plugin-unload name\n\n";
-	log::warning() << "Unload a loaded plugin from the irccd instance.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " plugin-unload logger" << std::endl;
-}
-
-void PluginUnload::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("plugin-unload requires 1 argument");
-
-	ctl.connection().send(json::object({
-		{ "command",	"plugin-unload"	},
-		{ "plugin",	args[0]		}
-	}).toJson(0));
-
-	ctl.connection().verify("plugin-unload");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-plugin-unload.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-plugin-unload.h -- implementation of irccdctl plugin-unload
- *
- * 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_PLUGIN_UNLOAD_H_
-#define _IRCCDCTL_COMMAND_PLUGIN_UNLOAD_H_
-
-/**
- * @file command-plugin-unload.h
- * @brief Implementation of irccdctl plugin-unload.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class PluginUnload
- * @brief Implementation of irccdctl plugin-unload.
- */
-class PluginUnload : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_PLUGIN_UNLOAD_H_
--- a/irccdctl/command-server-cmode.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-cmode.cpp -- implementation of irccdctl server-mode
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-cmode.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerChannelMode::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-cmode server channel mode\n\n";
-	log::warning() << "Change the mode of the specified channel.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-cmode freenode #staff +t" << std::endl;
-}
-
-void ServerChannelMode::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-cmode requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-cmode"	},
-		{ "server",	args[0]		},
-		{ "channel",	args[1]		},
-		{ "mode",	args[2]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-cmode");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-cmode.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-cmode.h -- implementation of irccdctl server-cmode
- *
- * 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_SERVER_CHANNEL_MODE_H_
-#define _IRCCDCTL_COMMAND_SERVER_CHANNEL_MODE_H_
-
-/**
- * @file command-server-cmode.h
- * @brief Implementation of irccdctl server-cmode.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerChannelMode
- * @brief Implementation of irccdctl server-cmode.
- */
-class ServerChannelMode : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_CHANNEL_MODE_H_
--- a/irccdctl/command-server-cnotice.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-cnotice.cpp -- implementation of irccdctl server-cnotice
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-cnotice.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerChannelNotice::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-cnotice server channel message\n\n";
-	log::warning() << "Send a message notice on a channel.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-cnotice freenode #staff \"Don't flood\"" << std::endl;
-}
-
-void ServerChannelNotice::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-cnotice requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-cnotice"	},
-		{ "server",	args[0]			},
-		{ "channel",	args[1]			},
-		{ "message",	args[2]			}
-	}).toJson(0));
-
-	ctl.connection().verify("server-cnotice");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-cnotice.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-cnotice.h -- implementation of irccdctl server-cnotice
- *
- * 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_SERVER_CNOTICE_H_
-#define _IRCCDCTL_COMMAND_SERVER_CNOTICE_H_
-
-/**
- * @file command-server-cnotice.h
- * @brief Implementation of irccdctl server-cnotice.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerChannelNotice
- * @brief Implementation of irccdctl server-cnotice.
- */
-class ServerChannelNotice : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_CNOTICE_H_
--- a/irccdctl/command-server-connect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * command-server-connect.cpp -- implementation of irccdctl server-connect
- *
- * 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 <options.h>
-
-#include "command-server-connect.h"
-
-namespace irccd {
-
-namespace command {
-
-namespace {
-
-parser::Result parse(std::vector<std::string> &args)
-{
-	parser::Options options{
-		{ "-c",			true	},
-		{ "--command",		true	},
-		{ "-n",			true	},
-		{ "--nickname",		true	},
-		{ "-r",			true	},
-		{ "--realname",		true	},
-		{ "-S",			false	},
-		{ "--ssl-verify",	false	},
-		{ "-s",			false	},
-		{ "--ssl",		false	},
-		{ "-u",			true	},
-		{ "--username",		true	}
-	};
-
-	return parser::read(args, options);
-}
-
-} // !namespace
-
-void ServerConnect::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-connect [options] id host [port]\n\n";
-	log::warning() << "Connect to a server.\n\n";
-	log::warning() << "Available options:\n";
-	log::warning() << "  -c, --command\t\tspecify the command char\n";
-	log::warning() << "  -n, --nickname\tspecify a nickname\n";
-	log::warning() << "  -r, --realname\tspecify a real name\n";
-	log::warning() << "  -S, --ssl-verify\tverify SSL\n";
-	log::warning() << "  -s, --ssl\t\tconnect using SSL\n";
-	log::warning() << "  -u, --username\tspecify a user name\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-connect -n jean example irc.example.org\n";
-	log::warning() << "\t" << sys::programName() << " server-connect --ssl example irc.example.org 6697" << std::endl;
-}
-
-void ServerConnect::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const
-{
-	std::vector<std::string> copy(args);
-
-	parser::Result result = parse(copy);
-	parser::Result::const_iterator it;
-
-	if (copy.size() < 2)
-		throw std::invalid_argument("server-connect requires at least 2 arguments");
-
-	json::Value object = json::object({
-		{ "command",	"server-connect"	},
-		{ "name",	copy[0]			},
-		{ "host",	copy[1]			}
-	});
-
-	/* Port */
-	if (copy.size() == 3) {
-		if (!util::isNumber(copy[2]))
-			throw std::invalid_argument("invalid port number");
-
-		object.insert("port", std::stoi(copy[2]));
-	}
-
-	/* SSL */
-	if (result.count("-S") > 0 || result.count("--ssl-verify") > 0)
-		object.insert("sslVerify", true);
-	if (result.count("-s") > 0 || result.count("--ssl") > 0)
-		object.insert("ssl", true);
-
-	/* Identity */
-	if ((it = result.find("-n")) != result.end() || (it = result.find("--nickname")) != result.end())
-		object.insert("nickname", it->second);
-	if ((it = result.find("-r")) != result.end() || (it = result.find("--realname")) != result.end())
-		object.insert("realname", it->second);
-	if ((it = result.find("-u")) != result.end() || (it = result.find("--username")) != result.end())
-		object.insert("username", it->second);
-
-	irccdctl.connection().send(object.toJson(0));
-	irccdctl.connection().verify("server-connect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-connect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-connect.h -- implementation of irccdctl server-connect
- *
- * 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_SERVER_CONNECT_H_
-#define _IRCCDCTL_COMMAND_SERVER_CONNECT_H_
-
-/**
- * @file command-server-connect.h
- * @brief Implementation of irccdctl server-connect.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerConnect
- * @brief Implementation of irccdctl server-connect.
- */
-class ServerConnect : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_CONNECT_H_
--- a/irccdctl/command-server-disconnect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-server-disconnect.cpp -- implementation of irccdctl server-disconnect
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-disconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerDisconnect::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-disconnect [server]\n\n";
-	log::warning() << "Disconnect from a server.\n\n";
-	log::warning() << "If server is not specified, irccd disconnects all servers.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-disconnect localhost" << std::endl;
-}
-
-void ServerDisconnect::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	json::Value object = json::object({
-		{ "command", "server-disconnect" }
-	});
-
-	if (args.size() > 0)
-		object.insert("server", args[0]);
-
-	ctl.connection().send(object.toJson(0));
-	ctl.connection().verify("server-disconnect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-disconnect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-disconnect.h -- implementation of irccdctl server-disconnect
- *
- * 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_SERVER_DISCONNECT_H_
-#define _IRCCDCTL_COMMAND_SERVER_DISCONNECT_H_
-
-/**
- * @file command-server-disconnect.h
- * @brief Implementation of irccdctl server-disconnect.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerDisconnect
- * @brief Implementation of irccdctl server-disconnect.
- */
-class ServerDisconnect : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_DISCONNECT_H_
--- a/irccdctl/command-server-info.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * command-server-info.cpp -- implementation of irccdctl server-info
- *
- * 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 "command-server-info.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerInfo::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-info server" << std::endl;
-}
-
-void ServerInfo::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 1)
-		throw std::invalid_argument("server-info requires 1 argument");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-info"	},
-		{ "server",	args[0]		}
-	}).toJson(0));
-
-	/* Show everything */
-	json::Value obj = ctl.connection().next("server-info");
-
-	if (obj.contains("error"))
-		throw std::runtime_error(obj.at("error").toString());
-
-	/* Server information */
-	std::cout << std::boolalpha;
-	std::cout << "Name           : " << obj.valueOr("name", "").toString(true) << std::endl;
-	std::cout << "Host           : " << obj.valueOr("host", "").toString(true) << std::endl;
-	std::cout << "Port           : " << obj.valueOr("port", "").toString(true) << std::endl;
-	std::cout << "Ipv6           : " << obj.valueOr("ipv6", "").toString(true) << std::endl;
-	std::cout << "SSL            : " << obj.valueOr("ssl", "").toString(true) << std::endl;
-	std::cout << "SSL verified   : " << obj.valueOr("sslVerify", "").toString(true) << std::endl;
-
-	/* Channels */
-	std::cout << "Channels       : ";
-
-	for (const json::Value &v : obj.valueOr("channels", json::Type::Array, json::array({})))
-		std::cout << v.toString() << " ";
-
-	std::cout << std::endl;
-
-	/* Identity */
-	std::cout << "Nickname       : " << obj.valueOr("nickname", "").toString(true) << std::endl;
-	std::cout << "User name      : " << obj.valueOr("username", "").toString(true) << std::endl;
-	std::cout << "Real name      : " << obj.valueOr("realname", "").toString(true) << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-info.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-info.h -- implementation of irccdctl server-info
- *
- * 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_SERVER_INFO_H_
-#define _IRCCDCTL_COMMAND_SERVER_INFO_H_
-
-/**
- * @file command-server-info.h
- * @brief Implementation of irccdctl server-info.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInfo
- * @brief Implementation of irccdctl server-info.
- */
-class ServerInfo : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !commands
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_INFO_H_
--- a/irccdctl/command-server-invite.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-invite.cpp -- implementation of irccdctl server-invite
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-invite.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerInvite::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-invite server nickname channel\n\n";
-	log::warning() << "Invite the specified target on the channel.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-invite freenode xorg62 #staff" << std::endl;
-}
-
-void ServerInvite::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-invite requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-invite"	},
-		{ "server",	args[0]		},
-		{ "target",	args[1]		},
-		{ "channel",	args[2]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-invite");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-invite.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-invite.h -- implementation of irccdctl server-invite
- *
- * 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_SERVER_INVITE_H_
-#define _IRCCDCTL_COMMAND_SERVER_INVITE_H_
-
-/**
- * @file command-server-invite.h
- * @brief Implementation of irccdctl server-invite.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerInvite
- * @brief Implementation of irccdctl server-invite.
- */
-class ServerInvite : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_INVITE_H_
--- a/irccdctl/command-server-join.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-join.cpp -- implementation of irccdctl server-join
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-join.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerJoin::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-join server channel [password]\n\n";
-	log::warning() << "Join the specified channel, the password is optional.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-join freenode #test\n";
-	log::warning() << "\t" << sys::programName() << " server-join freenode #private-club secret" << std::endl;
-}
-
-void ServerJoin::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 2)
-		throw std::invalid_argument("server-join requires at least 2 arguments");
-
-	json::Value object = json::object({
-		{ "command",	"server-join"	},
-		{ "server",	args[0]		},
-		{ "channel",	args[1]		}
-	});
-
-	if (args.size() == 3)
-		object.insert("password", args[2]);
-
-	ctl.connection().send(object.toJson(0));
-	ctl.connection().verify("server-join");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-join.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-join.h -- implementation of irccdctl server-join
- *
- * 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_SERVER_JOIN_H_
-#define _IRCCDCTL_COMMAND_SERVER_JOIN_H_
-
-/**
- * @file command-server-join.h
- * @brief Implementation of irccdctl server-join.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerJoin
- * @brief Implementation of irccdctl server-join.
- */
-class ServerJoin : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_JOIN_H_
--- a/irccdctl/command-server-kick.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-kick.cpp -- implementation of irccdctl server-kick
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-kick.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerKick::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-kick server target channel [reason]\n\n";
-	log::warning() << "Kick the specified target from the channel, the reason is optional.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-kick freenode jean #staff \"Stop flooding\"" << std::endl;
-}
-
-void ServerKick::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-kick requires at least 3 arguments ");
-
-	json::Value object = json::object({
-		{ "command",	"server-kick"	},
-		{ "server",	args[0]		},
-		{ "target",	args[1]		},
-		{ "channel",	args[2]		}
-	});
-
-	if (args.size() == 4)
-		object.insert("reason", args[3]);
-
-	ctl.connection().send(object.toJson(0));
-	ctl.connection().verify("server-kick");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-kick.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-kick.h -- implementation of irccdctl server-kick
- *
- * 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_SERVER_KICK_H_
-#define _IRCCDCTL_COMMAND_SERVER_KICK_H_
-
-/**
- * @file command-server-kick.h
- * @brief Implementation of irccdctl server-kick.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerKick
- * @brief Implementation of irccdctl server-kick.
- */
-class ServerKick : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_KICK_H_
--- a/irccdctl/command-server-list.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-list.cpp -- implementation of irccdctl server-list
- *
- * 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 "command-server-list.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerList::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-list\n\n";
-	log::warning() << "Get the list of all connected servers.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-list" << std::endl;
-}
-
-void ServerList::exec(Irccdctl &ctl, const std::vector<std::string> &) const
-{
-	ctl.connection().send(json::object({
-		{ "command", "server-list" }
-	}).toJson(0));
-
-	json::Value object = ctl.connection().next("server-list");
-	json::Value list = object.valueOr("list", json::Type::Array, json::array({}));
-
-	for (const auto &n : list)
-		std::cout << n.toString() << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-list.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-list.h -- implementation of irccdctl server-list
- *
- * 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_SERVER_LIST_H_
-#define _IRCCDCTL_COMMAND_SERVER_LIST_H_
-
-/**
- * @file command-server-list.h
- * @brief Implementation of irccdctl server-list.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerList
- * @brief Implementation of irccdctl server-list.
- */
-class ServerList : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_LIST_H_
--- a/irccdctl/command-server-me.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-me.cpp -- implementation of irccdctl server-me
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-me.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMe::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-me server target message\n\n";
-	log::warning() << "Send an action emote.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-me freenode #staff \"going back soon\"" << std::endl;
-}
-
-void ServerMe::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::runtime_error("server-me requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-me"	},
-		{ "server",	args[0]		},
-		{ "target",	args[1]		},
-		{ "message",	args[2]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-me");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-me.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-me.h -- implementation of irccdctl server-me
- *
- * 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_SERVER_ME_H_
-#define _IRCCDCTL_COMMAND_SERVER_ME_H_
-
-/**
- * @file command-server-me.h
- * @brief Implementation of irccdctl server-me.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMe
- * @brief Implementation of irccdctl server-me.
- */
-class ServerMe : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_ME_H_
--- a/irccdctl/command-server-message.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-message.cpp -- implementation of irccdctl server-message
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-message.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMessage::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-message server target message\n\n";
-	log::warning() << "Send a message to the specified target or channel.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-message freenode #staff \"Hello from irccd\"" << std::endl;
-}
-
-void ServerMessage::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-message requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-message"	},
-		{ "server",	args[0]			},
-		{ "target",	args[1]			},
-		{ "message",	args[2]			}
-	}).toJson(0));
-
-	ctl.connection().verify("server-message");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-message.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-message.h -- implementation of irccdctl server-message
- *
- * 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_SERVER_MESSAGE_H_
-#define _IRCCDCTL_COMMAND_SERVER_MESSAGE_H_
-
-/**
- * @file command-server-message.h
- * @brief Implementation of irccdctl server-message.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMessage
- * @brief Implementation of irccdctl server-message.
- */
-class ServerMessage : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_MESSAGE_H_
--- a/irccdctl/command-server-mode.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-server-mode.cpp-- implementation of irccdctl server-mode
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-mode.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerMode::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-mode server mode\n\n";
-	log::warning() << "Set the irccd's user mode.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-mode +i" << std::endl;
-}
-
-void ServerMode::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 2)
-		throw std::invalid_argument("server-mode requires 2 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-mode"	},
-		{ "server",	args[0]		},
-		{ "mode",	args[1]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-mode");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-mode.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-mode.h -- implementation of irccdctl server-mode
- *
- * 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_SERVER_MODE_H_
-#define _IRCCDCTL_COMMAND_SERVER_MODE_H_
-
-/**
- * @file command-server-mode.h
- * @brief Implementation of irccdctl server-mode.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerMode
- * @brief Implementation of irccdctl server-mode.
- */
-class ServerMode : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_MODE_H_
--- a/irccdctl/command-server-nick.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * command-server-nick.cpp -- implementation of irccdctl server-nick
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-nick.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerNick::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-nick server nickname\n\n";
-	log::warning() << "Change irccd's nickname.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-nick freenode david" << std::endl;
-}
-
-void ServerNick::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 2)
-		throw std::invalid_argument("server-nick requires 2 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-nick"	},
-		{ "server",	args[0]		},
-		{ "nickname",	args[1]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-nick");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-nick.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-nick.h -- implementation of irccdctl server-nick
- *
- * 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_SERVER_NICK_H_
-#define _IRCCDCTL_COMMAND_SERVER_NICK_H_
-
-/**
- * @file command-server-nick.h
- * @brief Implementation of irccdctl server-nick.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNick
- * @brief Implementation of irccdctl server-nick.
- */
-class ServerNick : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_NICK_H_
--- a/irccdctl/command-server-notice.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-notice.cpp -- implementation of irccdctl server-notice
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-notice.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerNotice::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-notice server target message\n\n";
-	log::warning() << "Send a private notice to the specified target.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-notice freenode jean \"I know you are here.\"" << std::endl;
-}
-
-void ServerNotice::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-notice requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-notice"	},
-		{ "server",	args[0]		},
-		{ "target",	args[1]		},
-		{ "message",	args[2]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-notice");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-notice.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-notice.h -- implementation of irccdctl server-notice
- *
- * 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_SERVER_NOTICE_H_
-#define _IRCCDCTL_COMMAND_SERVER_NOTICE_H_
-
-/**
- * @file command-server-cnotice.h
- * @brief Implementation of irccdctl server-notice.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerNotice
- * @brief Implementation of irccdctl server-notice.
- */
-class ServerNotice : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_NOTICE_H_
--- a/irccdctl/command-server-part.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * command-server-part.cpp -- implementation of irccdctl server-part
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-part.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerPart::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-part server channel [reason]\n\n";
-	log::warning() << "Leave the specified channel, the reason is optional.\n\n";
-	log::warning() << "Not all IRC servers support giving a reason to leave a channel, do not specify it if this is a concern.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-part freenode #staff\n";
-	log::warning() << "\t" << sys::programName() << " server-part freenode #botwar \"too noisy\"" << std::endl;
-}
-
-void ServerPart::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 2)
-		throw std::invalid_argument("server-part requires at least 2 arguments");
-
-	json::Value object = json::object({
-		{ "command",	"server-part"	},
-		{ "server",	args[0]		},
-		{ "channel",	args[1]		}
-	});
-
-	if (args.size() >= 3)
-		object.insert("reason", args[2]);
-
-	ctl.connection().send(object.toJson(0));
-	ctl.connection().verify("server-part");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-part.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-part.h -- implementation of irccdctl server-part
- *
- * 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_SERVER_PART_H_
-#define _IRCCDCTL_COMMAND_SERVER_PART_H_
-
-/**
- * @file command-server-part.h
- * @brief Implementation of irccdctl server-part.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerPart
- * @brief Implementation of irccdctl server-part.
- */
-class ServerPart : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_PART_H_
--- a/irccdctl/command-server-reconnect.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-reconnect.cpp -- implementation of irccdctl server-reconnect
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-reconnect.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerReconnect::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-reconnect [server]\n\n";
-	log::warning() << "Force reconnection of one or all servers.\n\n";
-	log::warning() << "If server is not specified, all servers will try to reconnect.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-reconnect\n";
-	log::warning() << "\t" << sys::programName() << " server-reconnect wanadoo" << std::endl;
-}
-
-void ServerReconnect::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	json::Value object = json::object({
-		{ "command", "server-reconnect" }
-	});
-
-	if (args.size() >= 1)
-		object.insert("server", args[0]);
-
-	ctl.connection().send(object.toJson(0));
-	ctl.connection().verify("server-reconnect");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-reconnect.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-reconnect.h -- implementation of irccdctl server-reconnect
- *
- * 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_SERVER_RECONNECT_H_
-#define _IRCCDCTL_COMMAND_SERVER_RECONNECT_H_
-
-/**
- * @file command-server-reconnect.h
- * @brief Implementation of irccdctl server-reconnect.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerReconnect
- * @brief Implementation of irccdctl server-reconnect.
- */
-class ServerReconnect : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_RECONNECT_H_
--- a/irccdctl/command-server-topic.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * command-server-topic.cpp -- implementation of irccdctl server-topic
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "command-server-topic.h"
-
-namespace irccd {
-
-namespace command {
-
-void ServerTopic::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " server-topic server channel topic\n\n";
-	log::warning() << "Change the topic of the specified channel.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t" << sys::programName() << " server-topic freenode #wmfs \"This is the best channel\"" << std::endl;
-}
-
-void ServerTopic::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	if (args.size() < 3)
-		throw std::invalid_argument("server-topic requires 3 arguments");
-
-	ctl.connection().send(json::object({
-		{ "command",	"server-topic"	},
-		{ "server",	args[0]		},
-		{ "channel",	args[1]		},
-		{ "topic",	args[2]		}
-	}).toJson(0));
-
-	ctl.connection().verify("server-topic");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-server-topic.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * command-server-topic.h -- implementation of irccdctl server-topic
- *
- * 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_SERVER_TOPIC_H_
-#define _IRCCDCTL_COMMAND_SERVER_TOPIC_H_
-
-/**
- * @file command-server-topic.h
- * @brief Implementation of irccdctl server-topic.
- */
-
-#include "command.h"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * @class ServerTopic
- * @brief Implementation of irccdctl server-topic.
- */
-class ServerTopic : public Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_SERVER_TOPIC_H_
--- a/irccdctl/command-watch.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,236 +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 <iostream>
-
-#include "command-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
-
-void Watch::usage(Irccdctl &) const
-{
-	log::warning() << "usage: " << sys::programName() << " watch [-f|--format native|json]\n\n";
-	log::warning() << "Start watching irccd events. You can use different output formats, native\n";
-	log::warning() << "is human readable format, json is pretty formatted json.\n\n";
-	log::warning() << "Example:\n";
-	log::warning() << "\t " << sys::programName() << " watch -f json" << std::endl;
-}
-
-void Watch::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
-{
-	std::vector<std::string> copy(args);
-	std::string format("native");
-
-	parser::Options options{
-		{ "-f",		true },
-		{ "--format",	true }
-	};
-
-	for (const auto &o : parser::read(copy, options))
-		if (o.first == "-f" || o.first == "--format")
-			format = o.second;
-
-	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");
-}
-
-} // !command
-
-} // !irccd
--- a/irccdctl/command-watch.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +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 Command {
-public:
-	/**
-	 * @copydoc Command::usage
-	 */
-	void usage(Irccdctl &irccdctl) const override;
-
-	/**
-	 * @copydoc Command::exec
-	 */
-	void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- a/irccdctl/command.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * command.h -- irccdctl 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 _IRCCDCTL_COMMAND_H_
-#define _IRCCDCTL_COMMAND_H_
-
-/**
- * @file command.h
- * @brief Irccdctl base command class.
- */
-
-#include <vector>
-#include <sstream>
-#include <string>
-
-#include <logger.h>
-#include <system.h>
-
-#include "irccdctl.h"
-
-namespace irccd {
-
-class Irccdctl;
-
-/**
- * @class Command
- * @brief Irccdctl base command class.
- */
-class Command {
-public:
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~Command() = default;
-
-	/**
-	 * Show the command line help.
-	 *
-	 * @param irccdctl the irccdctl instance
-	 */
-	virtual void usage(Irccdctl &irccdctl) const = 0;
-
-	/**
-	 * Execute the function with the command line given arguments.
-	 *
-	 * The function can throw anything.
-	 *
-	 * @param irccdctl the irccdctl instance
-	 * @param args the command line arguments
-	 */
-	virtual void exec(Irccdctl &irccdctl, const std::vector<std::string> &args) const = 0;
-};
-
-} // !irccd
-
-#endif // !_IRCCDCTL_COMMAND_H_
--- a/irccdctl/connection.cpp	Mon Mar 14 11:24:25 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 <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/irccdctl/connection.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +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 <elapsed-timer.h>
-#include <json.h>
-#include <sockets.h>
-#include <system.h>
-#include <util.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)
-	{
-		return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed());
-	}
-
-public:
-	/**
-	 * 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/irccdctl/irccdctl.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,502 +0,0 @@
-/*
- * irccdctl.cpp -- main irccdctl class
- *
- * 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 <cassert>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <unordered_map>
-
-#include <irccd-config.h>
-
-#include <elapsed-timer.h>
-#include <filesystem.h>
-#include <ini.h>
-#include <json.h>
-#include <logger.h>
-#include <options.h>
-#include <path.h>
-#include <sockets.h>
-#include <system.h>
-#include <util.h>
-
-#include "irccdctl.h"
-
-namespace irccd {
-
-using namespace net;
-using namespace net::address;
-using namespace net::option;
-using namespace net::protocol;
-
-using namespace std::placeholders;
-using namespace std::chrono_literals;
-
-/*
- * Config file format
- * ------------------------------------------------------------------
- *
- * [connect]
- * type = "ip | unix"
- *
- * # if ip
- * host = ""
- * port = number
- * domain = "ipv4 | ipv6", default: ipv4
- *
- * # if unix
- * path = ""
- *
- * [alias]
- * name = replacement
- */
-
-/*
- * Initialize a connection from the configuration file
- * ------------------------------------------------------------------
- */
-
-void Irccdctl::usage() const
-{
-	log::warning() << "usage: " << sys::programName() << " [options...] <command> [command-options...] [command-args...]\n\n";
-	log::warning() << "General options:\n";
-	log::warning() << "\tc, --config file\tspecify the configuration file\n";
-	log::warning() << "\t--help\t\t\tshow this help\n";
-	log::warning() << "\t-t, --type type\t\tspecify connection type\n";
-	log::warning() << "\t-v, --verbose\t\tbe verbose\n\n";
-	log::warning() << "Available options for type ip and ipv6 (-t, --type):\n";
-	log::warning() << "\t-h, --host address\tconnect to the specified address\n";
-	log::warning() << "\t-p, --port port\t\tuse the specified port number\n\n";
-	log::warning() << "Available options for type unix (-t, --type):\n";
-	log::warning() << "\t-P, --path file\t\tconnect to the specified socket file\n\n";
-	log::warning() << "General commands:\n";
-	log::warning() << "\thelp\t\t\tShow an help topic\n";
-	log::warning() << "\twatch\t\t\tStart listening to irccd\n\n";
-	log::warning() << "Plugin management:\n";
-	log::warning() << "\tplugin-info\t\tGet plugin information\n";
-	log::warning() << "\tplugin-list\t\tList all loaded plugins\n";
-	log::warning() << "\tplugin-load\t\tLoad a plugin\n";
-	log::warning() << "\tplugin-reload\t\tReload a plugin\n";
-	log::warning() << "\tplugin-unload\t\tUnload a plugin\n\n";
-	log::warning() << "Server management:\n";
-	log::warning() << "\tserver-cmode\t\tChange a channel mode\n";
-	log::warning() << "\tserver-cnotice\t\tSend a channel notice\n";
-	log::warning() << "\tserver-connect\t\tConnect to a server\n";
-	log::warning() << "\tserver-disconnect\tDisconnect from a server\n";
-	log::warning() << "\tserver-info\t\tGet server information\n";
-	log::warning() << "\tserver-invite\t\tInvite someone to a channel\n";
-	log::warning() << "\tserver-join\t\tJoin a channel\n";
-	log::warning() << "\tserver-kick\t\tKick someone from a channel\n";
-	log::warning() << "\tserver-list\t\tList all servers\n";
-	log::warning() << "\tserver-me\t\tSend a CTCP Action (same as /me)\n";
-	log::warning() << "\tserver-message\t\tSend a message to someone or a channel\n";
-	log::warning() << "\tserver-mode\t\tChange a user mode\n";
-	log::warning() << "\tserver-notice\t\tSend a private notice\n";
-	log::warning() << "\tserver-nick\t\tChange your nickname\n";
-	log::warning() << "\tserver-part\t\tLeave a channel\n";
-	log::warning() << "\tserver-reconnect\tReconnect one or all servers\n";
-	log::warning() << "\tserver-topic\t\tChange a channel topic\n";
-	log::warning() << "\nFor more information on a command, type " << sys::programName() << " help <command>" << std::endl;
-	std::exit(1);
-}
-
-void Irccdctl::readConnectIp(const ini::Section &sc)
-{
-	ini::Section::const_iterator it;
-
-	/* host */
-	std::string host;
-
-	if ((it = sc.find("host")) == sc.end())
-		throw std::invalid_argument("missing host parameter");
-
-	host = it->value();
-
-	/* port */
-	int port;
-
-	if ((it = sc.find("port")) == sc.end())
-		throw std::invalid_argument("missing port parameter");
-
-	try {
-		port = std::stoi(it->value());
-	} catch (...) {
-		throw std::invalid_argument("invalid port number: " + it->value());
-	}
-
-	/* domain */
-	Ip::Type domain{Ip::v4};
-
-	if ((it = sc.find("domain")) != sc.end()) {
-		if (it->value() == "ipv6")
-			domain = Ip::v6;
-		else if (it->value() == "ipv4")
-			domain = Ip::v4;
-		else
-			throw std::invalid_argument("invalid domain: " + it->value());
-	}
-
-	m_connection = std::make_unique<ConnectionBase<Ip>>(Ip{host, port, domain});
-}
-
-void Irccdctl::readConnectUnix(const ini::Section &sc)
-{
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-	auto it = sc.find("path");
-
-	if (it == sc.end())
-		throw std::invalid_argument("missing path parameter");
-
-	m_connection = std::make_unique<ConnectionBase<Local>>(Local{it->value(), false});
-#else
-	(void)sc;
-
-	throw std::invalid_argument("unix connection not supported on Windows");
-#endif
-}
-
-void Irccdctl::readConnect(const ini::Section &sc)
-{
-	auto it = sc.find("type");
-
-	if (it == sc.end())
-		throw std::invalid_argument("missing type parameter");
-
-	if (it->value() == "ip")
-		readConnectIp(sc);
-	else if (it->value() == "unix")
-		readConnectUnix(sc);
-	else
-		throw std::invalid_argument("invalid type given: " + it->value());
-}
-
-void Irccdctl::readGeneral(const ini::Section &sc)
-{
-	auto verbose = sc.find("verbose");
-
-	if (verbose != sc.end())
-		log::setVerbose(util::isBoolean(verbose->value()));
-}
-
-void Irccdctl::readAliases(const ini::Section &sc)
-{
-	for (const ini::Option &option : sc) {
-		/* This is the alias name */
-		Alias alias(option.key());
-
-		if (m_commands.count(option.key()) > 0)
-			throw std::invalid_argument("there is already a command named " + option.key());
-
-		/* Iterate over the list of commands to execute for this alias */
-		for (const std::string &repl : option) {
-			/* This is the alias split string */
-			std::vector<std::string> list = util::split(repl, " \t");
-
-			if (list.size() < 1)
-				throw std::invalid_argument("alias require at least one argument");
-
-			/* First argument is the command/alias to execute */
-			std::string command = list[0];
-
-			/* This is the alias arguments */
-			std::vector<AliasArg> args;
-
-			for (auto it = list.begin() + 1; it != list.end(); ++it)
-				args.push_back(std::move(*it));
-
-			alias.push_back({std::move(command), std::move(args)});
-		}
-
-		/* Show for debugging purpose */
-		log::debug() << "alias " << option.key() << ":" << std::endl;
-
-		for (const auto &cmd : alias) {
-			log::debug() << "  " << cmd.command() << " ";
-			log::debug() << util::join(cmd.args().begin(), cmd.args().end(), ' ') << std::endl;
-		}
-
-		m_aliases.emplace(option.key(), std::move(alias));
-	}
-}
-
-void Irccdctl::read(const std::string &path, const parser::Result &options)
-{
-	ini::Document doc(ini::File{path});
-	ini::Document::const_iterator it = doc.find("connect");
-
-	/* Do not try to read [connect] if specified at command line */
-	if (it != doc.end() && options.count("-t") == 0 && options.count("--type") == 0)
-		readConnect(*it);
-	if ((it = doc.find("general")) != doc.end())
-		readGeneral(*it);
-	if ((it = doc.find("alias")) != doc.end())
-		readAliases(*it);
-}
-
-/*
- * Initialize a connection from the command line
- * ------------------------------------------------------------------
- */
-
-void Irccdctl::parseConnectIp(const parser::Result &options, bool ipv6)
-{
-	parser::Result::const_iterator it;
-
-	/* host (-h or --host) */
-	std::string host;
-
-	if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end())
-		throw std::invalid_argument("missing host argument (-h or --host)");
-
-	host = it->second;
-
-	/* port (-p or --port) */
-	int port;
-
-	if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end())
-		throw std::invalid_argument("missing port argument (-p or --port)");
-
-	try {
-		port = std::stoi(it->second);
-	} catch (...) {
-		throw std::invalid_argument("invalid port number: " + it->second);
-	}
-
-	m_connection =  std::make_unique<ConnectionBase<Ip>>(Ip{host, port, (ipv6) ? Ip::v6 : Ip::v4});
-}
-
-void Irccdctl::parseConnectUnix(const parser::Result &options)
-{
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-	parser::Result::const_iterator it;
-
-	if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end())
-		throw std::invalid_argument("missing path parameter (-P or --path)");
-
-	m_connection = std::make_unique<ConnectionBase<Local>>(Local{it->second, false});
-#else
-	(void)options;
-
-	throw std::invalid_argument("unix connection not supported on Windows");
-#endif
-}
-
-void Irccdctl::parseConnect(const parser::Result &options)
-{
-	assert(options.count("-t") > 0 || options.count("--type") > 0);
-
-	auto it = options.find("-t");
-
-	if (it == options.end())
-		it = options.find("--type");
-	if (it->second == "ip" || it->second == "ipv6")
-		return parseConnectIp(options, it->second == "ipv6");
-	if (it->second == "unix")
-		return parseConnectUnix(options);
-
-	throw std::invalid_argument("invalid type given: " + it->second);
-}
-
-parser::Result Irccdctl::parse(int &argc, char **&argv) const
-{
-	/* 1. Parse command line options */
-	parser::Options def{
-		{ "-c",		true	},
-		{ "--config",	true	},
-		{ "-h",		true	},
-		{ "--help",	false	},
-		{ "--host",	true	},
-		{ "-p",		true	},
-		{ "--port",	true	},
-		{ "-P",		true	},
-		{ "--path",	true	},
-		{ "-t",		true	},
-		{ "--type",	true	},
-		{ "-v",		false	},
-		{ "--verbose",	false	}
-	};
-
-	parser::Result result;
-
-	try {
-		result = parser::read(argc, argv, def);
-
-		if (result.count("--help") != 0)
-			usage();
-			// NOTREACHED
-
-		if (result.count("-v") != 0 || result.count("--verbose") != 0)
-			log::setVerbose(true);
-	} catch (const std::exception &ex) {
-		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
-		usage();
-	}
-
-	return result;
-}
-
-void Irccdctl::exec(const Command &cmd, std::vector<std::string> args)
-{
-	cmd.exec(*this, args);
-}
-
-void Irccdctl::exec(const Alias &alias, std::vector<std::string> argsCopy)
-{
-	for (const AliasCommand &cmd : alias) {
-		std::vector<std::string> args(argsCopy);
-		std::vector<std::string> cmdArgs;
-		std::vector<std::string>::size_type toremove = 0;
-
-		/* 1. Append command name before */
-		cmdArgs.push_back(cmd.command());
-
-		for (const AliasArg &arg : cmd.args()) {
-			if (arg.isPlaceholder()) {
-				if (args.size() < arg.index() + 1)
-					throw std::invalid_argument("missing argument for placeholder %" + std::to_string(arg.index()));
-
-				cmdArgs.push_back(args[arg.index()]);
-
-				if (arg.index() + 1 > toremove)
-					toremove = arg.index() + 1;
-			} else {
-				cmdArgs.push_back(arg.value());
-			}
-		}
-
-		assert(toremove <= args.size());
-
-		/* 2. Remove the arguments that been placed in placeholders */
-		args.erase(args.begin(), args.begin() + toremove);
-
-		/* 3. Now append the rest of arguments */
-		std::copy(args.begin(), args.end(), std::back_inserter(cmdArgs));
-
-		/* 4. Finally try to execute */
-		exec(cmdArgs);
-	}
-}
-
-void Irccdctl::exec(std::vector<std::string> args)
-{
-	assert(args.size() > 0);
-
-	auto name = args[0];
-	auto alias = m_aliases.find(name);
-
-	/* Remove name */
-	args.erase(args.begin());
-
-	if (alias != m_aliases.end()) {
-		exec(alias->second, args);
-	} else {
-		auto cmd = m_commands.find(name);
-
-		if (cmd != m_commands.end())
-			exec(*cmd->second, args);
-		else
-			throw std::invalid_argument("no alias or command named " + name);
-	}
-}
-
-void Irccdctl::connect()
-{
-	log::info() << sys::programName() << ": connecting to irccd..." << std::endl;
-
-	/* Try to connect */
-	m_connection->connect(30000);
-
-	/* Get irccd information */
-	json::Value object = m_connection->next(30000);
-
-	if (!object.contains("program") || object.at("program").toString() != "irccd")
-		throw std::runtime_error("not an irccd server");
-
-	/* Get values */
-	m_major = object.at("major").toInt();
-	m_minor = object.at("minor").toInt();
-	m_patch = object.at("patch").toInt();
-	m_javascript = object.valueOr("javascript", json::Type::Boolean, false).toBool();
-	m_ssl = object.valueOr("ssl", json::Type::Boolean, false).toBool();
-
-	log::info() << std::boolalpha;
-	log::info() << sys::programName() << ": connected to irccd " << m_major << "." << m_minor << "." << m_patch << std::endl;
-	log::info() << sys::programName() << ": javascript: " << m_javascript << ", ssl supported: " << m_ssl << std::endl;
-}
-
-void Irccdctl::run(int argc, char **argv)
-{
-	/* 1. Read command line arguments */
-	parser::Result result = parse(argc, argv);
-
-	/*
-	 * 2. Open optional config by command line or by searching it
-	 *
-	 * The connection to irccd is searched in the following order :
-	 *
-	 * 1. From the command line if specified
-	 * 2. From the configuration file specified by -c
-	 * 3. From the configuration file searched through directories
-	 */
-	try {
-		if (result.count("-t") > 0 || result.count("--type") > 0)
-			parseConnect(result);
-
-		auto it = result.find("-c");
-
-		if (it != result.end() || (it = result.find("--config")) != result.end()) {
-			read(it->second, result);
-		} else {
-			for (const std::string &dir : path::list(path::PathConfig)) {
-				std::string path = dir + "irccdctl.conf";
-
-				if (fs::exists(path))
-					read(path, result);
-			}
-		}
-	} catch (const std::exception &ex) {
-		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
-		std::exit(1);
-	}
-
-	if (argc <= 0) {
-		usage();
-		// NOTREACHED
-	}
-
-	/* help does not require connection */
-	if (std::strcmp(argv[0], "help") != 0) {
-		if (!m_connection) {
-			log::warning() << sys::programName() << ": no connection specified" << std::endl;
-			std::exit(1);
-		}
-
-		connect();
-	}
-
-	/* Build a vector of arguments */
-	std::vector<std::string> args;
-
-	for (int i = 0; i < argc; ++i)
-		args.push_back(argv[i]);
-
-	exec(args);
-}
-
-} // !irccd
--- a/irccdctl/irccdctl.h	Mon Mar 14 11:24:25 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/*
- * irccdctl.h -- main irccdctl class
- *
- * 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_H_
-#define _IRCCDCTL_H_
-
-#include <cassert>
-#include <map>
-#include <memory>
-#include <string>
-
-#include <options.h>
-
-#include "alias.h"
-#include "command.h"
-#include "connection.h"
-
-namespace irccd {
-
-class Command;
-
-namespace ini {
-
-class Section;
-
-} // !ini
-
-class Irccdctl {
-private:
-	/* Irccd's information */
-	unsigned short m_major{0};
-	unsigned short m_minor{0};
-	unsigned short m_patch{0};
-
-	/* Irccd's compilation option */
-	bool m_javascript{true};
-	bool m_ssl{true};
-
-	std::unique_ptr<Connection> m_connection;
-	std::map<std::string, std::unique_ptr<Command>> m_commands;
-	std::map<std::string, Alias> m_aliases;
-
-	void usage() const;
-
-	void readConnectIp(const ini::Section &sc);
-	void readConnectUnix(const ini::Section &sc);
-	void readConnect(const ini::Section &sc);
-	void readGeneral(const ini::Section &sc);
-	void readAliases(const ini::Section &sc);
-	void read(const std::string &path, const parser::Result &options);
-
-	void parseConnectIp(const parser::Result &options, bool ipv6);
-	void parseConnectUnix(const parser::Result &options);
-	void parseConnect(const parser::Result &options);
-	parser::Result parse(int &argc, char **&argv) const;
-
-	void exec(const Command &cmd, std::vector<std::string> args);
-	void exec(const Alias &alias, std::vector<std::string> args);
-	void exec(std::vector<std::string> args);
-
-	void connect();
-
-public:
-	/**
-	 * Get the connection.
-	 *
-	 * @return the connection
-	 */
-	inline Connection &connection() noexcept
-	{
-		return *m_connection;
-	}
-
-	/**
-	 * Register a new command in irccdctl.
-	 *
-	 * @pre the command must not exist
-	 * @param key the command name
-	 */
-	template <typename Cmd>
-	inline void add(std::string key)
-	{
-		assert(m_commands.count(key) == 0);
-
-		m_commands.emplace(std::move(key), std::make_unique<Cmd>());
-	}
-
-	/**
-	 * Get all registered commands.
-	 *
-	 * @return the commands
-	 */
-	inline const std::map<std::string, std::unique_ptr<Command>> &commands() const noexcept
-	{
-		return m_commands;
-	}
-
-	void run(int argc, char **argv);
-};
-
-} // !irccd
-
-#endif // !_IRCCDCTL_H_
--- a/irccdctl/main.cpp	Mon Mar 14 11:24:25 2016 +0100
+++ b/irccdctl/main.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -16,41 +16,15 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "command-help.h"
-#include "command-watch.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 "irccdctl.h"
-#include "path.h"
+#include <irccd/irccdctl.h>
+#include <irccd/logger.h>
+#include <irccd/path.h>
 
 using namespace irccd;
-using namespace irccd::command;
 
 int main(int argc, char **argv)
 {
+	// TODO: move to Application
 	sys::setProgramName("irccdctl");
 	path::setApplicationPath(argv[0]);
 	log::setInterface(std::make_unique<log::Console>());
@@ -60,30 +34,6 @@
 	try {
 		Irccdctl ctl;
 
-		ctl.add<Help>("help");
-		ctl.add<PluginInfo>("plugin-info");
-		ctl.add<PluginList>("plugin-list");
-		ctl.add<PluginLoad>("plugin-load");
-		ctl.add<PluginReload>("plugin-reload");
-		ctl.add<PluginUnload>("plugin-unload");
-		ctl.add<ServerChannelMode>("server-cmode");
-		ctl.add<ServerChannelNotice>("server-cnotice");
-		ctl.add<ServerConnect>("server-connect");
-		ctl.add<ServerDisconnect>("server-disconnect");
-		ctl.add<ServerInfo>("server-info");
-		ctl.add<ServerInvite>("server-invite");
-		ctl.add<ServerJoin>("server-join");
-		ctl.add<ServerKick>("server-kick");
-		ctl.add<ServerList>("server-list");
-		ctl.add<ServerMe>("server-me");
-		ctl.add<ServerMessage>("server-message");
-		ctl.add<ServerMode>("server-mode");
-		ctl.add<ServerNick>("server-nick");
-		ctl.add<ServerNotice>("server-notice");
-		ctl.add<ServerPart>("server-part");
-		ctl.add<ServerReconnect>("server-reconnect");
-		ctl.add<ServerTopic>("server-topic");
-		ctl.add<Watch>("watch");
 		ctl.run(--argc, ++argv);
 	} catch (const std::exception &ex) {
 		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CMakeLists.txt	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,25 @@
+project(lib)
+
+include(irccd/CMakeSources.cmake)
+
+# TMP
+add_library(libirccd ${HEADERS} ${SOURCES})
+
+target_include_directories(
+	libirccd
+	PUBLIC
+		${irccd_BINARY_DIR}
+		${OPENSSL_INCLUDE_DIR}
+		${lib_SOURCE_DIR}
+)
+
+if (IRCCD_SYSTEM_WINDOWS)
+	list(APPEND LIBRARIES ws2_32 shlwapi)
+elseif (IRCCD_SYSTEM_MAC)
+	list(APPEND LIBRARIES resolv)
+endif ()
+
+target_link_libraries(libirccd extern-duktape extern-ircclient extern-jansson ${LIBRARIES})
+set_target_properties(libirccd PROPERTIES PREFIX "")
+
+# TODO: install with export stuff and all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/CMakeSources.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,55 @@
+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}
+	${JS_HEADERS}
+	${PRIVATE_HEADERS}
+	${CMAKE_CURRENT_LIST_DIR}/alias.h
+	${CMAKE_CURRENT_LIST_DIR}/application.h
+	${CMAKE_CURRENT_LIST_DIR}/config.h
+	${CMAKE_CURRENT_LIST_DIR}/irccd.h
+	${CMAKE_CURRENT_LIST_DIR}/irccdctl.h
+	${CMAKE_CURRENT_LIST_DIR}/json.h
+	${CMAKE_CURRENT_LIST_DIR}/logger.h
+	${CMAKE_CURRENT_LIST_DIR}/options.h
+	${CMAKE_CURRENT_LIST_DIR}/path.h
+	${CMAKE_CURRENT_LIST_DIR}/plugin.h
+	${CMAKE_CURRENT_LIST_DIR}/rule.h
+	${CMAKE_CURRENT_LIST_DIR}/server.h
+	${CMAKE_CURRENT_LIST_DIR}/server-state.h
+	${CMAKE_CURRENT_LIST_DIR}/system.h
+	${CMAKE_CURRENT_LIST_DIR}/timer.h
+	${CMAKE_CURRENT_LIST_DIR}/transport-client.h
+	${CMAKE_CURRENT_LIST_DIR}/transport-server.h
+	${CMAKE_CURRENT_LIST_DIR}/unicode.h
+	${CMAKE_CURRENT_LIST_DIR}/util.h
+)
+
+set(
+	SOURCES
+	${COMMAND_SOURCES}
+	${JS_SOURCES}
+	${PRIVATE_SOURCES}
+	${CMAKE_CURRENT_LIST_DIR}/alias.cpp
+	${CMAKE_CURRENT_LIST_DIR}/application.cpp
+	${CMAKE_CURRENT_LIST_DIR}/config.cpp
+	${CMAKE_CURRENT_LIST_DIR}/irccd.cpp
+	${CMAKE_CURRENT_LIST_DIR}/irccdctl.cpp
+	${CMAKE_CURRENT_LIST_DIR}/json.cpp
+	${CMAKE_CURRENT_LIST_DIR}/logger.cpp
+	${CMAKE_CURRENT_LIST_DIR}/options.cpp
+	${CMAKE_CURRENT_LIST_DIR}/path.cpp
+	${CMAKE_CURRENT_LIST_DIR}/plugin.cpp
+	${CMAKE_CURRENT_LIST_DIR}/rule.cpp
+	${CMAKE_CURRENT_LIST_DIR}/server.cpp
+	${CMAKE_CURRENT_LIST_DIR}/server-state.cpp
+	${CMAKE_CURRENT_LIST_DIR}/system.cpp
+	${CMAKE_CURRENT_LIST_DIR}/timer.cpp
+	${CMAKE_CURRENT_LIST_DIR}/transport-client.cpp
+	${CMAKE_CURRENT_LIST_DIR}/transport-server.cpp
+	${CMAKE_CURRENT_LIST_DIR}/unicode.cpp
+	${CMAKE_CURRENT_LIST_DIR}/util.cpp
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/alias.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,60 @@
+/*
+ * alias.cpp -- create irccdctl aliases
+ *
+ * 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 <regex>
+
+#include "alias.h"
+
+namespace irccd {
+
+AliasArg::AliasArg(std::string value)
+{
+	assert(!value.empty());
+
+	if ((m_isPlaceholder = std::regex_match(value, std::regex("^%\\d+$"))))
+		m_value = value.substr(1);
+	else
+		m_value = std::move(value);
+}
+
+unsigned AliasArg::index() const noexcept
+{
+	assert(isPlaceholder());
+
+	return std::stoi(m_value);
+}
+
+const std::string &AliasArg::value() const noexcept
+{
+	assert(!isPlaceholder());
+
+	return m_value;
+}
+
+std::ostream &operator<<(std::ostream &out, const AliasArg &arg)
+{
+	if (arg.m_isPlaceholder)
+		out << "%" << arg.m_value;
+	else
+		out << arg.m_value;
+
+	return out;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/alias.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,160 @@
+/*
+ * alias.h -- create irccdctl aliases
+ *
+ * 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_ALIAS_H_
+#define _IRCCDCTL_ALIAS_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * @class AliasArg
+ * @brief Describe an alias argument.
+ *
+ * When the user specify arguments, it can precise an applied argument or a placeholder that will be substituted
+ * during command line invocation.
+ *
+ * Placeholders are placed using %n where n is an integer starting from 0.
+ */
+class AliasArg {
+private:
+	std::string m_value;
+	bool m_isPlaceholder;
+
+public:
+	/**
+	 * Construct an argument.
+	 *
+	 * @pre value must not be empty
+	 * @param value the value
+	 */
+	AliasArg(std::string value);
+
+	/**
+	 * Check if the argument is a placeholder.
+	 *
+	 * @return true if the argument is a placeholder
+	 */
+	inline bool isPlaceholder() const noexcept
+	{
+		return m_isPlaceholder;
+	}
+
+	/**
+	 * Get the placeholder index (e.g %0 returns 0)
+	 *
+	 * @pre isPlaceholder() must return true
+	 * @return the position
+	 */
+	unsigned index() const noexcept;
+
+	/**
+	 * Get the real value.
+	 *
+	 * @pre isPlaceholder() must return false
+	 * @return the value
+	 */
+	const std::string &value() const noexcept;
+
+	friend std::ostream &operator<<(std::ostream &out, const AliasArg &);
+};
+
+/**
+ * @class AliasCommand
+ * @brief Describe a user-provided alias command.
+ *
+ * An alias command is just a command with a set of applied or placeholders arguments.
+ */
+class AliasCommand {
+private:
+	std::string m_command;
+	std::vector<AliasArg> m_args;
+
+public:
+	/**
+	 * Create an alias command.
+	 *
+	 * @param command the command
+	 * @param args the arguments
+	 */
+	inline AliasCommand(std::string command, std::vector<AliasArg> args = {}) noexcept
+		: m_command(std::move(command))
+		, m_args(std::move(args))
+	{
+	}
+
+	/**
+	 * Get the command to execute.
+	 *
+	 * @return the command name
+	 */
+	inline const std::string &command() const noexcept
+	{
+		return m_command;
+	}
+
+	/**
+	 * Get the arguments.
+	 *
+	 * @return the arguments
+	 */
+	inline const std::vector<AliasArg> &args() const noexcept
+	{
+		return m_args;
+	}
+};
+
+/**
+ * @class Alias
+ * @brief A set of commands to execute with their arguments.
+ *
+ * An alias is a composition of AliasCommand, typically, the user is able to set an alias that execute a list of
+ * specified commands in order they are defined.
+ */
+class Alias : public std::vector<AliasCommand> {
+private:
+	std::string m_name;
+
+public:
+	/**
+	 * Create an alias.
+	 *
+	 * @param name the alias name
+	 */
+	inline Alias(std::string name) noexcept
+		: m_name(std::move(name))
+	{
+	}
+
+	/**
+	 * Get the alias name.
+	 *
+	 * @return the name
+	 */
+	inline const std::string &name() const noexcept
+	{
+		return m_name;
+	}
+};
+
+} // !irccd
+
+#endif // !_IRCCDCTL_ALIAS_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/application.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,75 @@
+/*
+ * application.cpp -- super base class to create irccd front ends
+ *
+ * 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 "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"
+
+namespace irccd {
+
+Application::Application()
+{
+	/* Register all commands */
+	addCommand(std::make_unique<command::PluginInfo>());
+	addCommand(std::make_unique<command::PluginList>());
+	addCommand(std::make_unique<command::PluginLoad>());
+	addCommand(std::make_unique<command::PluginReload>());
+	addCommand(std::make_unique<command::PluginUnload>());
+	addCommand(std::make_unique<command::ServerChannelMode>());
+	addCommand(std::make_unique<command::ServerChannelNotice>());
+	addCommand(std::make_unique<command::ServerConnect>());
+	addCommand(std::make_unique<command::ServerDisconnect>());
+	addCommand(std::make_unique<command::ServerInfo>());
+	addCommand(std::make_unique<command::ServerInvite>());
+	addCommand(std::make_unique<command::ServerJoin>());
+	addCommand(std::make_unique<command::ServerKick>());
+	addCommand(std::make_unique<command::ServerList>());
+	addCommand(std::make_unique<command::ServerMe>());
+	addCommand(std::make_unique<command::ServerMessage>());
+	addCommand(std::make_unique<command::ServerMode>());
+	addCommand(std::make_unique<command::ServerNick>());
+	addCommand(std::make_unique<command::ServerNotice>());
+	addCommand(std::make_unique<command::ServerPart>());
+	addCommand(std::make_unique<command::ServerReconnect>());
+	addCommand(std::make_unique<command::ServerTopic>());
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/application.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,55 @@
+/*
+ * application.h -- super base class to create irccd front ends
+ *
+ * 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_APPLICATION_H
+#define IRCCD_APPLICATION_H
+
+#include <cassert>
+#include <memory>
+#include <unordered_map>
+
+#include "command/command.h"
+
+namespace irccd {
+
+using RemoteCommands = std::unordered_map<std::string, std::unique_ptr<RemoteCommand>>;
+
+class Application {
+protected:
+	RemoteCommands m_commands;
+
+public:
+	Application();
+
+	inline const RemoteCommands &commands() const noexcept
+	{
+		return m_commands;
+	}
+
+	inline void addCommand(std::unique_ptr<RemoteCommand> command)
+	{
+		assert(command);
+		assert(m_commands.count(command->name()) == 0);
+
+		m_commands.emplace(command->name(), std::move(command));
+	}
+};
+
+} // !irccd
+
+#endif // !_IRCCD_APPLICATION_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/CMakeSources.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,57 @@
+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
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/command.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,91 @@
+#include <sstream>
+
+#include <irccd/logger.h>
+#include <irccd/system.h>
+
+#include "command.h"
+
+namespace irccd {
+
+/*
+ * RemoteCommandRequest
+ * ------------------------------------------------------------------
+ */
+
+const std::string &RemoteCommandRequest::arg(unsigned index) const noexcept
+{
+	assert(index < m_args.size());
+
+	return m_args[index];
+}
+
+std::string RemoteCommandRequest::argOr(unsigned index, std::string defaultValue) const noexcept
+{
+	return index < m_args.size() ? m_args[index] : defaultValue;
+}
+
+const std::string &RemoteCommandRequest::option(const std::string &key) const noexcept
+{
+	assert(m_options.count(key) != 0);
+
+	return m_options.find(key)->second;
+}
+
+std::string RemoteCommandRequest::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;
+}
+
+/*
+ * RemoteCommand
+ * ------------------------------------------------------------------
+ */
+
+std::string RemoteCommand::usage() const
+{
+	std::ostringstream oss;
+
+	oss << "usage: sys::programName()";
+
+	/* Options summary */
+	if (options().size() > 0)
+		oss << " [options...]";
+
+	/* Arguments */
+	for (const RemoteCommandArg &arg : args()) {
+		if (!arg.second)
+			oss << "[";
+
+		oss << arg.first;
+
+		if (!arg.second)
+			oss << "]";
+	}
+
+	return oss.str();
+}
+
+json::Value RemoteCommand::request(Irccdctl &, const RemoteCommandRequest &) const
+{
+	return nullptr;
+}
+
+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/command.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,298 @@
+#ifndef _REMOTE_COMMAND_H_
+#define _REMOTE_COMMAND_H_
+
+#include <cassert>
+#include <cstdint>
+#include <map>
+#include <vector>
+
+#include <irccd/json.h>
+
+namespace irccd {
+
+class Irccd;
+class Irccdctl;
+
+/**
+ * @class RemoteCommandOption
+ * @brief Describe a command line option
+ */
+class RemoteCommandOption {
+public:
+	enum {
+		Argument = (1 << 0)	//!< option requires an argument
+	};
+
+private:
+	std::string m_id;
+	std::string m_simple;
+	std::string m_long;
+	std::string m_description;
+	std::uint8_t m_flags;
+
+public:
+	/**
+	 * Constructor an option description.
+	 *
+	 * @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 RemoteCommandOption(std::string id,
+				   std::string simpleKey,
+				   std::string longKey,
+				   std::string description,
+				   std::uint8_t flags = 0) noexcept
+		: m_id(std::move(id))
+		, m_simple(std::move(simpleKey))
+		, m_long(std::move(longKey))
+		, m_description(std::move(description))
+		, m_flags(flags)
+	{
+		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 flags.
+	 *
+	 * @return the flags
+	 */
+	inline std::uint8_t flags() const noexcept
+	{
+		return m_flags;
+	}
+};
+
+/**
+ * @brief List of command line options.
+ */
+using RemoteCommandOptions = std::vector<RemoteCommandOption>;
+
+using RemoteCommandArg = std::pair<std::string, bool>;
+
+/**
+ * @brief List of arguments to pass to the command.
+ *
+ * Any argument must have a non-empty name an can be optional if the boolean is set to false.
+ */
+using RemoteCommandArgs = std::vector<std::pair<std::string, bool>>;
+
+class RemoteCommandRequest {
+private:
+	std::multimap<std::string, std::string> m_options;
+	std::vector<std::string> m_args;
+
+public:
+	inline RemoteCommandRequest(std::multimap<std::string, std::string> options, std::vector<std::string> args) noexcept
+		: m_options(std::move(options))
+		, m_args(std::move(args))
+	{
+	}
+
+	inline const std::vector<std::string> &args() const noexcept
+	{
+		return m_args;
+	}
+
+	inline const std::multimap<std::string, std::string> &options() const noexcept
+	{
+		return m_options;
+	}
+
+	inline unsigned length() const noexcept
+	{
+		return m_args.size();
+	}
+
+	inline bool has(const std::string &option) const noexcept
+	{
+		return m_options.count(option) != 0;
+	}
+
+	const std::string &arg(unsigned index) const noexcept;
+
+	std::string argOr(unsigned index, std::string defaultValue) const noexcept;
+
+	const std::string &option(const std::string &key) const noexcept;
+
+	std::string optionOr(const std::string &key, std::string defaultValue) const noexcept;
+};
+
+class RemoteCommand {
+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 usage, without the prefix. (e.g. host port).
+	 *
+	 * Options are prepended automatically
+	 *
+	 * @return the usage
+	 */
+	std::string usage() const;
+
+	/**
+	 * Return the help message for irccdctl invocation.
+	 *
+	 * @return the help
+	 */
+	virtual std::string help() const = 0;
+
+	/**
+	 * Get the supported irccdctl options.
+	 *
+	 * @return the options
+	 */
+	virtual RemoteCommandOptions options() const
+	{
+		return RemoteCommandOptions();
+	}
+
+	/**
+	 * Get the supported arguments.
+	 *
+	 * @return the arguments
+	 */
+	virtual RemoteCommandArgs args() const
+	{
+		return RemoteCommandArgs();
+	}
+
+	/**
+	 * 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;
+};
+
+} // !irccd
+
+#endif // !_REMOTE_COMMAND_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/help.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,37 @@
+/*
+ * 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 "help.h"
+
+namespace irccd {
+
+namespace command {
+
+Help::Help()
+	: RemoteCommand("help", "General")
+{
+}
+
+std::string Help::help() const
+{
+	return "";
+}
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/help.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,51 @@
+/*
+ * 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();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/plugin-info.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,84 @@
+/*
+ * 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.";
+}
+
+RemoteCommandArgs 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 */
+	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/command/plugin-info.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs args() const;
+
+	/**
+	 * @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/command/plugin-list.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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 <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
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/plugin-list.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/command/plugin-load.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,63 @@
+/*
+ * 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.";
+}
+
+RemoteCommandArgs PluginLoad::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/plugin-load.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/plugin-reload.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,61 @@
+/*
+ * 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.";
+}
+
+RemoteCommandArgs PluginReload::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/plugin-reload.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/plugin-unload.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,61 @@
+/*
+ * 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.";
+}
+
+RemoteCommandArgs PluginUnload::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/plugin-unload.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-cmode.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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 <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 "";
+}
+
+RemoteCommandArgs ServerChannelMode::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-cmode.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-cnotice.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,58 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerChannelNotice::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-cnotice.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-connect.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,163 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandOptions ServerConnect::options() const
+{
+	return RemoteCommandOptions{
+		{ "command", "-c", "--command", "command character to use" },
+		{ "nickname", "-n", "--nickname", "nickname to use" },
+		{ "realname", "-r", "--realname", "realname to use" },
+		{ "sslverify", "-S", "--ssl-verify", "verify SSL" },
+		{ "ssl", "-s", "--ssl", "connect with SSL" },
+		{ "username", "-u", "--username", "username to use" }
+	};
+}
+
+RemoteCommandArgs ServerConnect::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-connect.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandOptions options() const override;
+
+	/**
+	 * @copydoc TransportCommand::args
+	 */
+	RemoteCommandArgs 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/command/server-disconnect.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,58 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerDisconnect::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-disconnect.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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;
+
+	RemoteCommandArgs 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/command/server-info.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,116 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerInfo::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-info.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-invite.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerInvite::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-invite.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-join.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,72 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerJoin::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-join.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-kick.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,74 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerKick::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-kick.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-list.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,60 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/server-list.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/command/server-me.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,67 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerMe::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-me.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-message.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,67 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerMessage::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-message.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-mode.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,62 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerMode::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-mode.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-nick.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,62 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerNick::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-nick.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-notice.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,68 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerNotice::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-notice.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-part.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,71 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerPart::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-part.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-reconnect.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,64 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerReconnect::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-reconnect.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/server-topic.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,67 @@
+/*
+ * 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 "";
+}
+
+RemoteCommandArgs ServerTopic::args() const
+{
+	return RemoteCommandArgs{
+		{ "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/command/server-topic.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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
+	 */
+	RemoteCommandArgs 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/command/watch.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,242 @@
+/*
+ * 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 <unordered_map>
+
+#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
+
+#if 0
+
+void Watch::usage(Irccdctl &) const
+{
+	log::warning() << "usage: " << sys::programName() << " watch [-f|--format native|json]\n\n";
+	log::warning() << "Start watching irccd events. You can use different output formats, native\n";
+	log::warning() << "is human readable format, json is pretty formatted json.\n\n";
+	log::warning() << "Example:\n";
+	log::warning() << "\t " << sys::programName() << " watch -f json" << std::endl;
+}
+
+void Watch::exec(Irccdctl &ctl, const std::vector<std::string> &args) const
+{
+	std::vector<std::string> copy(args);
+	std::string format("native");
+
+	parser::Options options{
+		{ "-f",		true },
+		{ "--format",	true }
+	};
+
+	for (const auto &o : parser::read(copy, options))
+		if (o.first == "-f" || o.first == "--format")
+			format = o.second;
+
+	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");
+}
+
+#endif
+
+} // !command
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/command/watch.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,51 @@
+/*
+ * 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();
+
+	/**
+	 * @copydoc RemoteCommand::help
+	 */
+	std::string help() const override;
+};
+
+} // !command
+
+} // !irccd
+
+#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/config.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,572 @@
+/*
+ * config.cpp -- irccd configuration loader
+ *
+ * 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>
+
+#if defined(HAVE_GETPID)
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <cerrno>
+#  include <cstring>
+#  include <fstream>
+#endif
+
+#if defined(HAVE_DAEMON)
+#  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 "irccd.h"
+
+using namespace std;
+using namespace std::string_literals;
+
+namespace irccd {
+
+void Config::loadGeneral(const ini::Document &config) const
+{
+	ini::Document::const_iterator sc = config.find("general");
+	ini::Section::const_iterator it;
+
+#if defined(HAVE_GETPID)
+	if (sc != config.end()) {
+		it = sc->find("pidfile");
+
+		if (it != sc->end() && !it->value().empty()) {
+			std::string path = it->value();
+			std::ofstream out(path, std::ofstream::trunc);
+
+			if (!out) {
+				log::warning() << "irccd: could not open pidfile " << path << ": " << std::strerror(errno) << std::endl;
+			} else {
+				log::debug() << "irccd: pid written in " << path << std::endl;
+				out << getpid();
+			}
+		}
+	}
+#endif
+
+#if defined(HAVE_DAEMON)
+	/* CLI priority is higher */
+	bool daemonize = m_options.count("-f") == 0 && m_options.count("--foreground") == 0;
+
+	if (daemonize && sc != config.end()) {
+		it = sc->find("foreground");
+
+		if (it != sc->end())
+			daemonize = !util::isBoolean(it->value());
+	}
+
+	if (daemonize)
+		daemon(1, 0);
+#endif
+
+	if (sc != config.end()) {
+		try {
+#if defined(HAVE_SETGID)
+			if ((it = sc->find("gid")) != sc->end())
+				sys::setGid(it->value());
+#endif
+#if defined(HAVE_SETUID)
+			if ((it = sc->find("uid")) != sc->end())
+				sys::setUid(it->value());
+#endif
+		} catch (const std::exception &ex) {
+			log::warning() << "irccd: could not set " << it->key() << ": " << ex.what() << std::endl;
+		}
+	}
+}
+
+void Config::loadLogFile(const ini::Section &sc) const
+{
+	/*
+	 * TODO: improve that with CMake options.
+	 */
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	string normal = "log.txt";
+	string errors = "errors.txt";
+#else
+	string normal = "/var/log/irccd/log.txt";
+	string errors = "/var/log/irccd/errors.txt";
+#endif
+
+	ini::Section::const_iterator it;
+
+	if ((it = sc.find("path-logs")) != sc.end())
+		normal = it->value();
+	if ((it = sc.find("path-errors")) != sc.end())
+		errors = it->value();
+
+	log::setInterface(make_unique<log::File>(move(normal), move(errors)));
+}
+
+void Config::loadLogSyslog() const
+{
+#if defined(HAVE_SYSLOG)
+	log::setInterface(make_unique<log::Syslog>());
+#else
+	log::warning() << "irccd: syslog is not available on this platform" << endl;
+#endif // !HAVE_SYSLOG
+}
+
+void Config::loadLogs(const ini::Document &config) const
+{
+	ini::Document::const_iterator sc = config.find("logs");
+
+	if (sc == config.end())
+		return;
+
+	ini::Section::const_iterator it;
+
+	if ((it = sc->find("verbose")) != sc->end() && m_options.count("-v") == 0 && m_options.count("--verbose"))
+		log::setVerbose(util::isBoolean(it->value()));
+	if ((it = sc->find("type")) != sc->end()) {
+		/* Console is the default, no test case */
+		if (it->value() == "file")
+			loadLogFile(*sc);
+		else if (it->value() == "syslog")
+			loadLogSyslog();
+		else
+			log::warning() << "irccd: unknown log type: " << it->value() << std::endl;
+	}
+}
+
+void Config::loadPlugins(Irccd &irccd, const ini::Section &sc) const
+{
+#if defined(WITH_JS)
+	for (const ini::Option &option : sc) {
+		try {
+			if (option.value().empty())
+				irccd.loadPlugin(option.key(), option.key(), true);
+			else
+				irccd.loadPlugin(option.key(), option.value(), false);
+		} catch (const std::exception &ex) {
+			log::warning() << "plugin " << option.key() << ": " << ex.what() << std::endl;
+		}
+	}
+#else
+	(void)irccd;
+	(void)sc;
+#endif
+}
+
+void Config::loadPluginConfig(Irccd &irccd, const ini::Section &sc, string name) const
+{
+#if defined(WITH_JS)
+	PluginConfig config;
+
+	for (const ini::Option &option : sc)
+		config.emplace(option.key(), option.value());
+
+	irccd.addPluginConfig(std::move(name), std::move(config));
+#else
+	(void)irccd;
+	(void)sc;
+	(void)name;
+#endif
+}
+
+void Config::loadPlugins(Irccd &irccd, const ini::Document &config) const
+{
+#if defined(WITH_JS)
+	std::regex regex("^plugin\\.([A-Za-z0-9-_]+)$");
+	std::smatch match;
+
+	/*
+	 * Load plugin configurations before we load plugins since we use them
+	 * when we load the plugin itself.
+	 */
+	for (const ini::Section &section : config)
+		if (regex_match(section.key(), match, regex))
+			loadPluginConfig(irccd, section, match[1]);
+
+	ini::Document::const_iterator it = config.find("plugins");
+
+	if (it != config.end())
+		loadPlugins(irccd, *it);
+#else
+	(void)irccd;
+	(void)config;
+
+	log::warning() << "irccd: JavaScript disabled, ignoring plugins" << std::endl;
+#endif
+}
+
+void Config::loadServer(Irccd &irccd, const ini::Section &sc) const
+{
+	ServerInfo info;
+	ServerIdentity identity;
+	ServerSettings settings;
+
+	/* Name */
+	ini::Section::const_iterator it;
+
+	if ((it = sc.find("name")) == sc.end()) 
+		throw std::invalid_argument("server: missing name");
+	else if (!util::isIdentifierValid(it->value()))
+		throw std::invalid_argument("server " + it->value() + ": name is not valid");
+	else if (irccd.hasServer(it->value()))
+		throw std::invalid_argument("server " + it->value() + ": already exists");
+
+	info.name = it->value();
+
+	/* Host */
+	if ((it = sc.find("host")) == sc.end())
+		throw std::invalid_argument("server " + info.name + ": missing host");
+
+	info.host = it->value();
+
+	/* Optional identity */
+	if ((it = sc.find("identity")) != sc.end())
+		identity = irccd.findIdentity(it->value());
+
+	/* Optional port */
+	if ((it = sc.find("port")) != sc.end()) {
+		try {
+			info.port = std::stoi(it->value());
+		} catch (const std::exception &) {
+			throw std::invalid_argument("server " + info.name + ": invalid port number: " + it->value());
+		}
+	}
+
+	/* Optional password */
+	if ((it = sc.find("password")) != sc.end())
+		info.password = it->value();
+
+	/* Optional flags */
+	if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value()))
+		info.flags |= ServerInfo::Ipv6;
+	if ((it = sc.find("ssl")) != sc.end())
+		if (util::isBoolean(it->value()))
+#if defined(WITH_SSL)
+			info.flags |= ServerInfo::Ssl;
+#else
+			throw std::invalid_argument("server " + info.name + ": ssl is disabled");
+#endif
+
+	if ((it = sc.find("ssl-verify")) != sc.end())
+		if (util::isBoolean(it->value()))
+#if defined(WITH_SSL)
+			info.flags |= ServerInfo::SslVerify;
+#else
+			throw std::invalid_argument("server " + info.name + ": ssl is disabled");
+#endif
+
+	/* Options */
+	if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value()))
+		settings.flags |= ServerSettings::AutoRejoin;
+	if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value()))
+		settings.flags |= ServerSettings::JoinInvite;
+
+	/* Channels */
+	if ((it = sc.find("channels")) != sc.end()) {
+		for (const std::string &s : *it) {
+			ServerChannel channel;
+
+			if (auto pos = s.find(":") != std::string::npos) {
+				channel.name = s.substr(0, pos);
+				channel.password = s.substr(pos + 1);
+			} else {
+				channel.name = s;
+			}
+
+			settings.channels.push_back(std::move(channel));
+		}
+	}
+	if ((it = sc.find("command-char")) != sc.end())
+		settings.command = it->value();
+
+	/* Reconnect */
+	try {
+		if ((it = sc.find("reconnect-tries")) != sc.end())
+			settings.recotries = std::stoi(it->value());
+		if ((it = sc.find("reconnect-timeout")) != sc.end())
+			settings.recotimeout = std::stoi(it->value());
+	} catch (const std::exception &) {
+		throw std::invalid_argument("server " + info.name + ": invalid number for " + it->key() + ": " + it->value());
+	}
+
+	irccd.addServer(std::make_shared<Server>(std::move(info), std::move(identity), std::move(settings)));
+}
+
+void Config::loadServers(Irccd &irccd, const ini::Document &config) const
+{
+	for (const ini::Section &section : config) {
+		if (section.key() == "server") {
+			try {
+				loadServer(irccd, section);
+			} catch (const exception &ex) {
+				log::warning() << ex.what() << endl;
+			}
+		}
+	}
+}
+
+void Config::loadIdentity(Irccd &irccd, const ini::Section &sc) const
+{
+	ServerIdentity identity;
+	ini::Section::const_iterator it;
+
+	if ((it = sc.find("name")) == sc.end()) {
+		throw invalid_argument("missing name");
+	} else if (!util::isIdentifierValid(it->value())) {
+		throw invalid_argument("identity name not valid");
+	}
+
+	identity.name = it->value();
+
+	/* Optional stuff */
+	if ((it = sc.find("username")) != sc.end())
+		identity.username = it->value();
+	if ((it = sc.find("realname")) != sc.end())
+		identity.realname = it->value();
+	if ((it = sc.find("nickname")) != sc.end())
+		identity.nickname = it->value();
+	if ((it = sc.find("ctcp-version")) != sc.end())
+		identity.ctcpversion = it->value();
+
+	log::debug() << "identity " << identity.name << ": ";
+	log::debug() << "nickname=" << identity.nickname << ", username=" << identity.username << ", ";
+	log::debug() << "realname=" << identity.realname << ", ctcp-version=" << identity.ctcpversion << endl;
+
+	irccd.addIdentity(move(identity));
+}
+
+void Config::loadIdentities(Irccd &irccd, const ini::Document &config) const
+{
+	for (const ini::Section &section : config) {
+		if (section.key() == "identity") {
+			try {
+				loadIdentity(irccd, section);
+			} catch (const exception &ex) {
+				log::warning() << "identity: " << ex.what() << endl;
+			}
+		}
+	}
+}
+
+void Config::loadRule(Irccd &irccd, const ini::Section &sc) const
+{
+	/* Simple converter from std::vector to std::unordered_set */
+	auto toSet = [] (const std::vector<std::string> &v) -> std::unordered_set<std::string> {
+		return std::unordered_set<std::string>(v.begin(), v.end());
+	};
+
+	RuleSet servers, channels, origins, plugins, events;
+	RuleAction action = RuleAction::Accept;
+
+	/* Get the sets */
+	ini::Section::const_iterator it;
+
+	if ((it = sc.find("servers")) != sc.end())
+		servers = toSet(*it);
+	if ((it = sc.find("channels")) != sc.end())
+		channels = toSet(*it);
+	if ((it = sc.find("origins")) != sc.end())
+		origins = toSet(*it);
+	if ((it = sc.find("plugins")) != sc.end())
+		plugins = toSet(*it);
+	if ((it = sc.find("channels")) != sc.end())
+		channels = toSet(*it);
+
+	/* Get the action */
+	if ((it = sc.find("action")) == sc.end())
+		throw std::invalid_argument("missing action parameter");
+	if (it->value() == "drop")
+		action = RuleAction::Drop;
+	else if (it->value() == "accept")
+		action = RuleAction::Accept;
+	else
+		throw std::invalid_argument("invalid action given: " + it->value());
+
+	irccd.addRule(Rule(move(servers), move(channels), move(origins), move(plugins), move(events), action));
+}
+
+void Config::loadRules(Irccd &irccd, const ini::Document &config) const
+{
+	for (const ini::Section &sc : config) {
+		if (sc.key() == "rule") {
+			try {
+				loadRule(irccd, sc);
+			} catch (const std::exception &ex) {
+				log::warning() << "rule: " << ex.what() << std::endl;
+			}
+		}
+	}
+}
+
+void Config::loadTransportIp(Irccd &irccd, const ini::Section &sc) const
+{
+	bool ipv6 = true;
+	bool ipv4 = true;
+
+	ini::Section::const_iterator it;
+
+	/* Port */
+	int port;
+
+	if ((it = sc.find("port")) == sc.end())
+		throw invalid_argument("missing port");
+
+	try {
+		port = stoi(it->value());
+	} catch (const std::exception &) {
+		throw std::invalid_argument("invalid port number: " + it->value());
+	}
+
+	/* Address*/
+	std::string address = "*";
+
+	if ((it = sc.find("address")) != sc.end())
+		address = it->value();
+
+	/* Domain */
+	if ((it = sc.find("domain")) != sc.end()) {
+		ipv6 = false;
+		ipv4 = false;
+
+		for (const string &v : *it) {
+			if (v == "ipv4")
+				ipv4 = true;
+			if (v == "ipv6")
+				ipv6 = true;
+		}
+	}
+
+	if (ipv6)
+		irccd.addTransport(std::make_shared<TransportServerIp>(AF_INET6, move(address), port, !ipv4));
+	else if (ipv4)
+		irccd.addTransport(std::make_shared<TransportServerIp>(AF_INET, move(address), port));
+	else
+		throw std::invalid_argument("domain must at least have ipv4 or ipv6");
+}
+
+void Config::loadTransportUnix(Irccd &irccd, const ini::Section &sc) const
+{
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+	/* Path */
+	ini::Section::const_iterator it = sc.find("path");
+
+	if (it == sc.end()) {
+		throw std::invalid_argument("missing path parameter");
+	} else {
+		string path = sc["path"].value();
+
+		irccd.addTransport(std::make_shared<TransportServerUnix>(move(path)));
+	}
+#else
+	(void)irccd;
+	(void)sc;
+
+	throw std::invalid_argument("local transport not supported on on this platform");
+#endif
+}
+
+void Config::loadTransports(Irccd &irccd, const ini::Document &config) const
+{
+	for (const ini::Section &sc : config) {
+		if (sc.key() == "transport") {
+			try {
+				ini::Section::const_iterator it = sc.find("type");
+
+				if (it == sc.end())
+					log::warning() << "transport: missing type parameter" << std::endl;
+				else if (it->value() == "ip")
+					loadTransportIp(irccd, sc);
+				else if (it->value() == "unix")
+					loadTransportUnix(irccd, sc);
+				else
+					log::warning() << "transport: invalid type given: " << std::endl;
+			} catch (const net::Error &error) {
+				log::warning() << "transport: " << error.function() << ": " << error.what() << std::endl;
+			} catch (const exception &ex) {
+				log::warning() << "transport: error: " << ex.what() << endl;
+			}
+		}
+	}
+}
+
+bool Config::openConfig(Irccd &irccd, const string &path) const
+{
+	try {
+		/*
+		 * Order matters, take care when you change this.
+		 */
+		ini::Document config(ini::File{path});
+
+		loadGeneral(config);
+		loadLogs(config);
+		loadIdentities(irccd, config);
+		loadServers(irccd, config);
+		loadRules(irccd, config);
+		loadPlugins(irccd, config);
+		loadTransports(irccd, config);
+	} catch (const ini::Error &ex) {
+		log::warning() << sys::programName() << ": " << path << ":" << ex.line() << ":" << ex.column() << ": " << ex.what() << std::endl;
+		return false;
+	} catch (const std::exception &ex) {
+		log::warning() << sys::programName() << ": " << path << ": " << ex.what() << std::endl;
+		return false;
+	}
+
+	return true;
+}
+
+Config::Config(parser::Result options) noexcept
+	: m_options(move(options))
+{
+}
+
+void Config::load(Irccd &irccd)
+{
+	auto it = m_options.find("-c");
+	auto found = false;
+
+	if (it != m_options.end())
+		found = openConfig(irccd, it->second);
+	else if ((it = m_options.find("--config")) != m_options.end())
+		found = openConfig(irccd, it->second);
+	else {
+		/* Search for a configuration file */
+		for (const string &path : path::list(path::PathConfig)) {
+			string fullpath = path + "irccd.conf";
+
+			log::info() << "irccd: trying " << fullpath << endl;
+
+			if (openConfig(irccd, fullpath)) {
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found) {
+		log::warning() << "irccd: no configuration file could be found, exiting" << endl;
+		exit(1);
+	}
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/config.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,85 @@
+/*
+ * config.h -- irccd configuration loader
+ *
+ * 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_CONFIG_LOADER_H_
+#define _IRCCD_CONFIG_LOADER_H_
+
+/**
+ * @file Config.h
+ * @brief Read .ini configuration file for irccd
+ */
+
+#include <irccd/options.h>
+
+namespace irccd {
+
+namespace ini {
+
+class Document;
+class Section;
+
+} // !ini
+
+class Irccd;
+
+/**
+ * @class Config
+ * @brief Read .ini configuration file for irccd
+ */
+class Config {
+private:
+	parser::Result m_options;
+
+	void loadGeneral(const ini::Document &config) const;
+	void loadLogFile(const ini::Section &sc) const;
+	void loadLogSyslog() const;
+	void loadLogs(const ini::Document &config) const;
+	void loadPlugins(Irccd &irccd, const ini::Section &sc) const;
+	void loadPluginConfig(Irccd &irccd, const ini::Section &sc, std::string name) const;
+	void loadPlugins(Irccd &irccd, const ini::Document &config) const;
+	void loadServer(Irccd &irccd, const ini::Section &sc) const;
+	void loadServers(Irccd &irccd, const ini::Document &config) const;
+	void loadIdentity(Irccd &irccd, const ini::Section &sc) const;
+	void loadIdentities(Irccd &irccd, const ini::Document &config) const;
+	void loadRule(Irccd &irccd, const ini::Section &sc) const;
+	void loadRules(Irccd &irccd, const ini::Document &config) const;
+	void loadTransportIp(Irccd &irccd, const ini::Section &sc) const;
+	void loadTransportUnix(Irccd &irccd, const ini::Section &sc) const;
+	void loadTransports(Irccd &irccd, const ini::Document &config) const;
+	bool openConfig(Irccd &irccd, const std::string &path) const;
+
+public:
+	/**
+	 * Construct the configuration file loader. If path is empty, then the configuration file is searched through
+	 * the standard directories.
+	 *
+	 * @param options the option parsed at command line
+	 */
+	Config(parser::Result options) noexcept;
+
+	/**
+	 * Load the config into irccd.
+	 *
+	 * @param irccd the irccd instance
+	 */
+	void load(Irccd &irccd);
+};
+
+} // !irccd
+
+#endif // !_IRCCD_CONFIG_LOADER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/irccd.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,1057 @@
+/*
+ * irccd.cpp -- main irccd class
+ *
+ * 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 <stdexcept>
+
+#include "private/filesystem.h"
+
+#include "irccd.h"
+#include "logger.h"
+#include "path.h"
+#include "util.h"
+
+using namespace std;
+using namespace std::placeholders;
+using namespace std::string_literals;
+
+namespace irccd {
+
+void Irccd::handleServerChannelMode(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string mode, std::string arg)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onChannelMode:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  mode: " << mode << "\n";
+	log::debug() << "  argument: " << arg << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onChannelMode"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "mode",	mode			},
+		{ "argument",	arg			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onChannelMode";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onChannelMode(std::move(server), std::move(origin), std::move(channel), std::move(mode), std::move(arg));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerChannelNotice(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onChannelNotice:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onChannelNotice"	},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "message",	message			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onChannelNotice";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onChannelNotice(std::move(server), std::move(origin), std::move(channel), std::move(message));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerConnect(std::weak_ptr<Server> ptr)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onConnect" << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onConnect"		},
+		{ "server",	server->info().name	}
+	});
+
+	postServerEvent({server->info().name, /* origin */ "", /* channel */ "", json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onConnect";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onConnect(std::move(server));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerInvite(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onInvite:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  target: " << target << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onInvite"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onInvite";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onInvite(std::move(server), std::move(origin), std::move(channel));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerJoin(std::weak_ptr<Server> ptr, std::string origin, std::string channel)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onJoin:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onJoin"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onJoin";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onJoin(std::move(server), std::move(origin), std::move(channel));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerKick(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string target, std::string reason)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onKick:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  target: " << target << "\n";
+	log::debug() << "  reason: " << reason << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onKick"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "target",	target			},
+		{ "reason",	reason			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onKick";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onKick(std::move(server), std::move(origin), std::move(channel), std::move(target), std::move(reason));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerMessage(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onMessage:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onMessage"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "message",	message			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &plugin) -> std::string {
+			return util::parseMessage(message, server->settings().command, plugin.info().name).second == util::MessageType::Command ? "onCommand" : "onMessage";
+		}
+		, [=] (Plugin &plugin) {
+			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.info().name);
+
+			if (pack.second == util::MessageType::Command)
+				plugin.onCommand(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
+			else
+				plugin.onMessage(std::move(server), std::move(origin), std::move(channel), std::move(pack.first));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerMe(std::weak_ptr<Server> ptr, std::string origin, std::string target, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onMe:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  target: " << target << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onMe"			},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "target",	target			},
+		{ "message",	message			}
+	});
+
+	postServerEvent({server->info().name, origin, target, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onMe";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onMe(std::move(server), std::move(origin), std::move(target), std::move(message));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerMode(std::weak_ptr<Server> ptr, std::string origin, std::string mode)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onMode\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  mode: " << mode << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onMode"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "mode",	mode			}
+	});
+
+	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onMode";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onMode(std::move(server), std::move(origin), std::move(mode));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerNames(std::weak_ptr<Server> ptr, std::string channel, std::set<std::string> nicknames)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onNames:\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  names: " << util::join(nicknames.begin(), nicknames.end(), ", ") << std::endl;
+
+	json::Value names(std::vector<json::Value>(nicknames.begin(), nicknames.end()));
+	json::Value json = json::object({
+		{ "event",	"onNames"		},
+		{ "server",	server->info().name	},
+		{ "channel",	channel			},
+		{ "names",	std::move(names)	}
+	});
+
+	postServerEvent({server->info().name, /* origin */ "", channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onNames";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onNames(std::move(server), std::move(channel), std::vector<std::string>(nicknames.begin(), nicknames.end()));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerNick(std::weak_ptr<Server> ptr, std::string origin, std::string nickname)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onNick:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  nickname: " << nickname << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onNick"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "nickname",	nickname		}
+	});
+
+	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onNick";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onNick(std::move(server), std::move(origin), std::move(nickname));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerNotice(std::weak_ptr<Server> ptr, std::string origin, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onNotice:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onNotice"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "message",	message			}
+	});
+
+	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onNotice";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onNotice(std::move(server), std::move(origin), std::move(message));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerPart(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string reason)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onPart:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  reason: " << reason << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onPart"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "reason",	reason			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onPart";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onPart(std::move(server), std::move(origin), std::move(channel), std::move(reason));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerQuery(std::weak_ptr<Server> ptr, std::string origin, std::string message)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onQuery:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  message: " << message << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onQuery"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "message",	message			}
+	});
+
+	postServerEvent({server->info().name, origin, /* channel */ "", json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &plugin) -> std::string {
+			return util::parseMessage(message, server->settings().command, plugin.info().name).second == util::MessageType::Command ? "onQueryCommand" : "onQuery";
+		}
+		, [=] (Plugin &plugin) {
+			util::MessagePair pack = util::parseMessage(message, server->settings().command, plugin.info().name);
+
+			if (pack.second == util::MessageType::Command)
+				plugin.onQueryCommand(std::move(server), std::move(origin), std::move(pack.first));
+			else
+				plugin.onQuery(std::move(server), std::move(origin), std::move(pack.first));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerTopic(std::weak_ptr<Server> ptr, std::string origin, std::string channel, std::string topic)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onTopic:\n";
+	log::debug() << "  origin: " << origin << "\n";
+	log::debug() << "  channel: " << channel << "\n";
+	log::debug() << "  topic: " << topic << std::endl;
+
+	json::Value json = json::object({
+		{ "event",	"onTopic"		},
+		{ "server",	server->info().name	},
+		{ "origin",	origin			},
+		{ "channel",	channel			},
+		{ "topic",	topic			}
+	});
+
+	postServerEvent({server->info().name, origin, channel, json.toJson(0)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onTopic";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onTopic(std::move(server), std::move(origin), std::move(channel), std::move(topic));
+		}
+#endif
+	});
+}
+
+void Irccd::handleServerWhois(std::weak_ptr<Server> ptr, ServerWhois whois)
+{
+	std::shared_ptr<Server> server = ptr.lock();
+
+	if (!server)
+		return;
+
+	log::debug() << "server " << server->info().name << ": event onWhois\n";
+	log::debug() << "  nickname: " << whois.nick << "\n";
+	log::debug() << "  username: " << whois.user << "\n";
+	log::debug() << "  host: " << whois.host << "\n";
+	log::debug() << "  realname: " << whois.realname << "\n";
+	log::debug() << "  channels: " << util::join(whois.channels.begin(), whois.channels.end()) << std::endl;
+
+	json::Value object = json::object({
+		{ "server",	server->info().name	},
+		{ "nickname",	whois.nick		},
+		{ "username",	whois.user		},
+		{ "host",	whois.host		},
+		{ "realname",	whois.realname		}
+	});
+
+	postServerEvent({server->info().name, /* origin */ "", /* channel */ "", object.toJson(-1)
+#if defined(WITH_JS)
+		, [=] (Plugin &) -> std::string {
+			return "onWhois";
+		}
+		, [=] (Plugin &plugin) {
+			plugin.onWhois(std::move(server), std::move(whois));
+		}
+#endif
+	});
+}
+
+void Irccd::handleTransportCommand(std::weak_ptr<TransportClient> ptr, const json::Value &object)
+{
+	assert(object.isObject());
+
+	post([=] () {
+		/* 0. Be sure the object still exists */
+		auto tc = ptr.lock();
+
+		if (!tc)
+			return;
+
+		/* 1. Check if the Json object is valid */
+		auto name = object.find("command");
+		if (name == object.end() || name->typeOf() != json::Type::String) {
+			// TODO: send error
+			log::warning() << "invalid command object" << std::endl;
+			return;
+		}
+
+		/* 2. Search for a command */
+		auto it = m_commands.find(name->toString());
+
+		if (it == m_commands.end()) {
+			// TODO: send error again
+			log::warning() << "command does not exists" << std::endl;
+			return;
+		}
+
+		/* 3. Try to execute it */
+		json::Value response;
+
+		try {
+			response = it->second->exec(*this, object);
+
+			/* Adjust if command has returned something else */
+			if (!response.isObject())
+				response = json::object({});
+			
+			response.insert("command", it->first);
+			response.insert("status", "true");
+		} catch (const std::exception &ex) {
+			response.insert("status", "false");
+			response.insert("error", ex.what());
+		}
+
+		/* 4. Send the result */
+		tc->send(response.toJson(0));
+	});
+}
+
+void Irccd::handleTransportDie(std::weak_ptr<TransportClient> ptr)
+{
+	post([=] () {
+		log::info() << "transport: client disconnected" << std::endl;
+
+		auto tc = ptr.lock();
+
+		if (tc)
+			m_lookupTransportClients.erase(tc->handle());
+	});
+}
+
+void Irccd::processIpc(fd_set &input)
+{
+	if (FD_ISSET(m_socketServer.handle(), &input)) {
+		try {
+			(void)m_socketServer.recv(8);
+		} catch (const exception &) {
+			// TODO: think what we can do here
+		}
+	}
+}
+
+void Irccd::processTransportClients(fd_set &input, fd_set &output)
+{
+	for (auto &pair : m_lookupTransportClients)
+		pair.second->sync(input, output);
+}
+
+void Irccd::processTransportServers(fd_set &input)
+{
+	for (auto &pair : m_lookupTransportServers) {
+		if (!FD_ISSET(pair.second->handle(), &input))
+			continue;
+
+		log::debug() << "transport: new client connected" << endl;
+
+		std::shared_ptr<TransportClient> client = pair.second->accept();
+		std::weak_ptr<TransportClient> ptr(client);
+
+		/* Send some information */
+		json::Value object = json::object({
+			{ "program",	"irccd"			},
+			{ "major",	IRCCD_VERSION_MAJOR	},
+			{ "minor",	IRCCD_VERSION_MINOR	},
+			{ "patch",	IRCCD_VERSION_PATCH	}
+		});
+
+#if defined(WITH_JS)
+		object.insert("javascript", true);
+#endif
+#if defined(WITH_SSL)
+		object.insert("ssl", true);
+#endif
+
+		client->send(object.toJson(0));
+
+		/* Connect signals */
+		client->onCommand.connect(std::bind(&Irccd::handleTransportCommand, this, ptr, _1));
+		client->onDie.connect(std::bind(&Irccd::handleTransportDie, this, ptr));
+
+		/* Register it */
+		m_lookupTransportClients.emplace(client->handle(), move(client));
+	}
+}
+
+void Irccd::processServers(fd_set &input, fd_set &output)
+{
+	for (auto &pair : m_servers)
+		pair.second->sync(input, output);
+}
+
+void Irccd::process(fd_set &setinput, fd_set &setoutput)
+{
+	/* 1. May be IPC */
+	processIpc(setinput);
+
+	/* 2. Check for transport clients */
+	processTransportClients(setinput, setoutput);
+
+	/* 3. Check for transport servers */
+	processTransportServers(setinput);
+
+	/* 4. Check for servers */
+	processServers(setinput, setoutput);
+}
+
+void Irccd::postServerEvent(ServerEvent event) noexcept
+{
+#if defined(WITH_JS)
+	post([=] () {
+		for (auto &pair : m_plugins) {
+			auto name = event.name(*pair.second);
+			auto allowed = Rule::solve(m_rules, event.server, event.target, event.origin, pair.first, name);
+
+			if (!allowed) {
+				log::debug() << "rule: event skipped on match" << std::endl;
+				continue;
+			} else {
+				log::debug() << "rule: event allowed" << std::endl;
+			}
+
+			try {
+				event.exec(*pair.second);
+			} catch (const js::ErrorInfo &info) {
+				log::warning() << "plugin " << pair.second->info().name << ": error: " << info.what() << std::endl;
+
+				if (!info.fileName.empty())
+					log::warning() << "    " << info.fileName << ":" << info.lineNumber << std::endl;
+				if (!info.stack.empty())
+					log::warning() << "    " << info.stack << std::endl;
+			}
+		}
+	});
+#endif
+
+	/* Asynchronous send */
+	for (auto &pair : m_lookupTransportClients)
+		pair.second->send(event.json);
+}
+
+Irccd::Irccd()
+{
+	/* Bind a socket to any port */
+	m_socketServer.set(net::option::SockReuseAddress{true});
+	m_socketServer.bind(net::address::Ip{"*", 0});
+	m_socketServer.listen(1);
+
+	/* Do the socket pair */
+	m_socketClient.connect(net::address::Ip{"127.0.0.1", m_socketServer.address().port()});
+	m_socketServer = m_socketServer.accept(nullptr);
+	m_socketClient.set(net::option::SockBlockMode{false});
+}
+
+void Irccd::post(Event ev) noexcept
+{
+	std::lock_guard<mutex> lock(m_mutex);
+
+	m_events.push_back(move(ev));
+
+	/* Silently discard */
+	try {
+		m_socketClient.send(" ");
+	} catch (...) {
+	}
+}
+
+void Irccd::addServer(shared_ptr<Server> server) noexcept
+{
+	assert(m_servers.count(server->info().name) == 0);
+
+	std::weak_ptr<Server> ptr(server);
+
+	server->onChannelMode.connect(std::bind(&Irccd::handleServerChannelMode, this, ptr, _1, _2, _3, _4));
+	server->onChannelNotice.connect(std::bind(&Irccd::handleServerChannelNotice, this, ptr, _1, _2, _3));
+	server->onConnect.connect(std::bind(&Irccd::handleServerConnect, this, ptr));
+	server->onInvite.connect(std::bind(&Irccd::handleServerInvite, this, ptr, _1, _2, _3));
+	server->onJoin.connect(std::bind(&Irccd::handleServerJoin, this, ptr, _1, _2));
+	server->onKick.connect(std::bind(&Irccd::handleServerKick, this, ptr, _1, _2, _3, _4));
+	server->onMessage.connect(std::bind(&Irccd::handleServerMessage, this, ptr, _1, _2, _3));
+	server->onMe.connect(std::bind(&Irccd::handleServerMe, this, ptr, _1, _2, _3));
+	server->onMode.connect(std::bind(&Irccd::handleServerMode, this, ptr, _1, _2));
+	server->onNames.connect(std::bind(&Irccd::handleServerNames, this, ptr, _1, _2));
+	server->onNick.connect(std::bind(&Irccd::handleServerNick, this, ptr, _1, _2));
+	server->onNotice.connect(std::bind(&Irccd::handleServerNotice, this, ptr, _1, _2));
+	server->onPart.connect(std::bind(&Irccd::handleServerPart, this, ptr, _1, _2, _3));
+	server->onQuery.connect(std::bind(&Irccd::handleServerQuery, this, ptr, _1, _2));
+	server->onTopic.connect(std::bind(&Irccd::handleServerTopic, this, ptr, _1, _2, _3));
+	server->onWhois.connect(std::bind(&Irccd::handleServerWhois, this, ptr, _1));
+	server->onDie.connect([this, ptr] () {
+		post([=] () {
+			auto server = ptr.lock();
+
+			if (server) {
+				log::info() << "server " << server->info().name << ": removed" << std::endl;
+				m_servers.erase(server->info().name);
+			}
+		});
+	});
+
+	m_servers.emplace(server->info().name, move(server));
+}
+
+std::shared_ptr<Server> Irccd::getServer(const std::string &name) const noexcept
+{
+	auto it = m_servers.find(name);
+
+	if (it == m_servers.end())
+		return nullptr;
+
+	return it->second;
+}
+
+std::shared_ptr<Server> Irccd::requireServer(const std::string &name) const
+{
+	auto it = m_servers.find(name);
+
+	if (it == m_servers.end())
+		throw std::invalid_argument("server " + name + " not found");
+
+	return it->second;
+}
+
+void Irccd::removeServer(const std::string &name)
+{
+	auto it = m_servers.find(name);
+
+	if (it != m_servers.end()) {
+		it->second->disconnect();
+		m_servers.erase(it);
+	}
+}
+
+void Irccd::clearServers() noexcept
+{
+	for (auto &pair : m_servers)
+		pair.second->disconnect();
+
+	m_servers.clear();
+}
+
+void Irccd::addTransport(std::shared_ptr<TransportServer> ts)
+{
+	m_lookupTransportServers.emplace(ts->handle(), ts);
+}
+
+#if defined(WITH_JS)
+
+std::shared_ptr<Plugin> Irccd::getPlugin(const std::string &name) const noexcept
+{
+	auto it = m_plugins.find(name);
+
+	if (it == m_plugins.end())
+		return nullptr;
+
+	return it->second;
+}
+
+std::shared_ptr<Plugin> Irccd::requirePlugin(const std::string &name) const
+{
+	auto it = m_plugins.find(name);
+
+	if (it == m_plugins.end())
+		throw std::out_of_range(std::string("plugin ") + name + " not found");
+
+	return it->second;
+}
+
+void Irccd::addPlugin(std::shared_ptr<Plugin> plugin)
+{
+	std::weak_ptr<Plugin> ptr(plugin);
+
+	plugin->onTimerSignal.connect(std::bind(&Irccd::handleTimerSignal, this, ptr, _1));
+	plugin->onTimerEnd.connect(std::bind(&Irccd::handleTimerEnd, this, ptr, _1));
+
+	/* Store reference to irccd */
+	plugin->context().putGlobal("\xff""\xff""irccd", js::RawPointer<Irccd>{this});
+
+	/* Initial load now */
+	try {
+		plugin->onLoad();
+		m_plugins.insert({plugin->info().name, plugin});
+	} catch (const std::exception &ex) {
+		log::info() << "plugin " << plugin->info().name << ": " << ex.what() << std::endl;
+	}
+}
+
+void Irccd::loadPlugin(std::string name, const std::string &source, bool find)
+{
+	if (m_plugins.count(name) > 0)
+		throw std::invalid_argument("plugin already loaded");
+
+	std::vector<string> paths;
+	std::shared_ptr<Plugin> plugin;
+
+	if (find)
+		for (const std::string &dir : path::list(path::PathPlugins))
+			paths.push_back(dir + source + ".js");
+	else
+		paths.push_back(source);
+
+	/* Iterate over all paths */
+	log::info() << "plugin " << name << ": trying to load:" << std::endl;
+
+	for (const auto &path : paths) {
+		log::info() << "  from " << path << std::endl;
+
+		try {
+			plugin = std::make_shared<Plugin>(name, path, m_pluginConf[name]);
+			break;
+		} catch (const std::exception &ex) {
+			log::info() << "    error: " << ex.what() << std::endl;
+		}
+	}
+
+	if (plugin)
+		addPlugin(std::move(plugin));
+	else
+		throw std::runtime_error("no suitable plugin found");
+}
+
+void Irccd::reloadPlugin(const std::string &name)
+{
+	auto plugin = getPlugin(name);
+
+	if (plugin)
+		plugin->onReload();
+}
+
+void Irccd::unloadPlugin(const std::string &name)
+{
+	auto plugin = getPlugin(name);
+
+	if (plugin) {
+		plugin->onUnload();
+		m_plugins.erase(name);
+	}
+}
+
+#endif // !WITH_JS
+
+/*
+ * Timer slots
+ * ------------------------------------------------------------------
+ *
+ * These slots are called from timer threads.
+ */
+
+#if defined(WITH_JS)
+
+void Irccd::handleTimerSignal(std::weak_ptr<Plugin> ptr, std::shared_ptr<Timer> timer)
+{
+	post([this, ptr, timer] () {
+		auto plugin = ptr.lock();
+
+		if (!plugin)
+			return;
+
+		auto &ctx = plugin->context();
+
+		js::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();
+		} catch (const std::exception &) {
+			log::info() << "failure" << std::endl;
+		}
+	});
+}
+
+void Irccd::handleTimerEnd(std::weak_ptr<Plugin> ptr, std::shared_ptr<Timer> timer)
+{
+	post([this, ptr, timer] () {
+		auto plugin = ptr.lock();
+
+		if (plugin) {
+			log::debug() << "timer: finished, removing from plugin `" << plugin->info().name << "'" << std::endl;
+			plugin->removeTimer(timer);
+		}
+	});
+}
+
+#endif
+
+void Irccd::run()
+{
+	while (m_running) {
+		poll();
+		dispatch();
+	}
+}
+
+void Irccd::poll()
+{
+	fd_set setinput;
+	fd_set setoutput;
+	auto max = m_socketServer.handle();
+	auto set = [&] (fd_set &set, net::Handle handle) {
+		FD_SET(handle, &set);
+
+		if (handle > max)
+			max = handle;
+	};
+
+	FD_ZERO(&setinput);
+	FD_ZERO(&setoutput);
+
+	/* 1. Add master socket */
+	FD_SET(m_socketServer.handle(), &setinput);
+
+	/* 2. Add servers */
+	for (auto &pair : m_servers) {
+		pair.second->update();
+		pair.second->prepare(setinput, setoutput, max);
+	}
+
+	/* 3. Add transports clients */
+	for (auto &pair : m_lookupTransportClients) {
+		set(setinput, pair.first);
+
+		if (pair.second->hasOutput())
+			set(setoutput, pair.first);
+	}
+
+	/* 4. Add transport servers */
+	for (auto &pair : m_lookupTransportServers)
+		set(setinput, pair.first);
+
+	/* 5. Do the selection */
+	struct timeval tv;
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 250000;
+
+	int error = select(max + 1, &setinput, &setoutput, nullptr, &tv);
+
+	/* Skip anyway */
+	if (!m_running)
+		return;
+
+	/* Skip on error */
+	if (error < 0 && errno != EINTR) {
+		log::warning() << "irccd: " << net::error(error) << endl;
+		return;
+	}
+
+	process(setinput, setoutput);
+}
+
+void Irccd::dispatch()
+{
+	/*
+	 * Make a copy because the events can add other events while we are iterating it. Also lock because the timers
+	 * may alter these events too.
+	 */
+	std::vector<Event> copy;
+
+	{
+		std::lock_guard<mutex> lock(m_mutex);
+
+		copy = move(m_events);
+
+		/* Clear for safety */
+		m_events.clear();
+	}
+
+	if (copy.size() > 0)
+		log::debug() << "irccd: dispatching " << copy.size() << " event" << (copy.size() > 1 ? "s" : "") << endl;
+
+	for (auto &ev : copy)
+		ev();
+}
+
+void Irccd::stop()
+{
+	log::debug() << "irccd: requesting to stop now" << endl;
+
+	m_running = false;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/irccd.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,536 @@
+/*
+ * irccd.h -- main irccd class
+ *
+ * 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_H_
+#define _IRCCD_H_
+
+#include <atomic>
+#include <cassert>
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <irccd-config.h>
+
+#include <irccd/private/sockets.h>
+
+#if defined(WITH_JS)
+#  include "plugin.h"
+#endif
+
+#include "application.h"
+#include "logger.h"
+#include "rule.h"
+#include "server.h"
+#include "transport-server.h"
+
+namespace irccd {
+
+class Plugin;
+class TransportCommand;
+
+/**
+ * Event to execute after the poll.
+ */
+using Event = std::function<void ()>;
+
+/**
+ * List of events.
+ */
+using Events = std::vector<Event>;
+
+/**
+ * Map of identities.
+ */
+using Identities = std::unordered_map<std::string, ServerIdentity>;
+
+/**
+ * List of rules.
+ */
+using Rules = std::vector<Rule>;
+
+/**
+ * @class ServerEvent
+ * @brief Structure that owns several informations about an IRC event
+ *
+ * This structure is used to dispatch the IRC event to the plugins and the transports.
+ */
+class ServerEvent {
+public:
+	std::string server;
+	std::string origin;
+	std::string target;
+	std::string json;
+#if defined(WITH_JS)
+	std::function<std::string (Plugin &)> name;
+	std::function<void (Plugin &)> exec;
+#endif
+};
+
+/**
+ * Map of servers.
+ */
+using Servers = std::unordered_map<std::string, std::shared_ptr<Server>>;
+
+/**
+ * Map of transport command handlers.
+ */
+using TransportCommands = std::unordered_map<std::string, std::unique_ptr<TransportCommand>>;
+
+#if defined(WITH_JS)
+
+/**
+ * Map of plugins.
+ */
+using Plugins = std::unordered_map<std::string, std::shared_ptr<Plugin>>;
+
+/**
+ * Map of plugin configurations.
+ */
+using PluginConfigs = std::unordered_map<std::string, PluginConfig>;
+
+#endif
+
+/**
+ * @class Irccd
+ * @brief Irccd main instance
+ *
+ * This class is used as the main application event loop, it stores servers, plugins and transports.
+ *
+ * In a general manner, no code in irccd is thread-safe because irccd is mono-threaded except the JavaScript timer
+ * API.
+ *
+ * If you plan to add more threads to irccd, then the simpliest and safest way to execute thread-safe code is to
+ * register an event using Irccd::post function which will be called during the event loop dispatching.
+ *
+ * Thus, except noticed as thread-safe, no function is assumed to be.
+ */
+class Irccd : public Application {
+private:
+	template <typename T>
+	using LookupTable = std::unordered_map<net::Handle, std::shared_ptr<T>>;
+
+	/* Main loop */
+	std::atomic<bool> m_running{true};
+
+	/* Mutex for post() */
+	std::mutex m_mutex;
+
+	/* IPC */
+	net::SocketTcp<net::address::Ip> m_socketServer;
+	net::SocketTcp<net::address::Ip> m_socketClient;
+
+	/* Event loop */
+	Events m_events;
+
+	/* Servers */
+	Servers m_servers;
+
+	/* Optional JavaScript plugins */
+#if defined(WITH_JS)
+	Plugins m_plugins;
+	PluginConfigs m_pluginConf;
+#endif
+
+	/* Identities */
+	Identities m_identities;
+
+	/* Rules */
+	Rules m_rules;
+
+	/* Lookup tables */
+	LookupTable<TransportClient> m_lookupTransportClients;
+	LookupTable<TransportServer> m_lookupTransportServers;
+
+	/*
+	 * Server slots
+	 * ----------------------------------------------------------
+	 */
+
+	void handleServerChannelMode(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg);
+	void handleServerChannelNotice(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string notice);
+	void handleServerConnect(std::weak_ptr<Server> server);
+	void handleServerInvite(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target);
+	void handleServerJoin(std::weak_ptr<Server> server, std::string origin, std::string channel);
+	void handleServerKick(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason);
+	void handleServerMessage(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string message);
+	void handleServerMe(std::weak_ptr<Server> server, std::string origin, std::string target, std::string message);
+	void handleServerMode(std::weak_ptr<Server> server, std::string origin, std::string mode);
+	void handleServerNames(std::weak_ptr<Server> server, std::string channel, std::set<std::string> nicknames);
+	void handleServerNick(std::weak_ptr<Server> server, std::string origin, std::string nickname);
+	void handleServerNotice(std::weak_ptr<Server> server, std::string origin, std::string message);
+	void handleServerPart(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string reason);
+	void handleServerQuery(std::weak_ptr<Server> server, std::string origin, std::string message);
+	void handleServerTopic(std::weak_ptr<Server> server, std::string origin, std::string channel, std::string topic);
+	void handleServerWhois(std::weak_ptr<Server> server, ServerWhois whois);
+
+	/*
+	 * Transport clients slots
+	 * ----------------------------------------------------------
+	 */
+	void handleTransportCommand(std::weak_ptr<TransportClient>, const json::Value &);
+	void handleTransportDie(std::weak_ptr<TransportClient>);
+
+	/*
+	 * Plugin timers slots
+	 * ----------------------------------------------------------
+	 *
+	 * These handlers catch the timer signals and call the plugin function or remove the timer from the plugin.
+	 */
+
+#if defined(WITH_JS)
+	void handleTimerSignal(std::weak_ptr<Plugin>, std::shared_ptr<Timer>);
+	void handleTimerEnd(std::weak_ptr<Plugin>, std::shared_ptr<Timer>);
+#endif
+
+	/*
+	 * Process the socket sets.
+	 * ----------------------------------------------------------
+	 *
+	 * These functions are called after polling which sockets are ready for reading/writing.
+	 */
+
+	void processIpc(fd_set &input);
+	void processTransportClients(fd_set &input, fd_set &output);
+	void processTransportServers(fd_set &input);
+	void processServers(fd_set &input, fd_set &output);
+	void process(fd_set &setinput, fd_set &setoutput);
+
+public:
+	/**
+	 * Constructor that instanciate IPC.
+	 */
+	Irccd();
+
+	/**
+	 * Load a configuration into irccd. Added as convenience to allow expressions like `irccd.load(Config{"foo"})`.
+	 *
+	 * @param config the configuration loader
+	 */
+	template <typename T>
+	inline void load(T &&config)
+	{
+		config.load(*this);
+	}
+
+	/**
+	 * Add an event to the queue. This will immediately signals the event loop to interrupt itself to dispatch
+	 * the pending events.
+	 *
+	 * @param ev the event
+	 * @note Thread-safe
+	 */
+	void post(Event ev) noexcept;
+
+	/*
+	 * This function wraps post() to iterate over all plugins to call the function and to send to all
+	 * connected transport the event.
+	 */
+	void postServerEvent(ServerEvent) noexcept;
+
+	/*
+	 * Identity management
+	 * ----------------------------------------------------------
+	 *
+	 * Functions to get or add new identities.
+	 */
+
+	/**
+	 * Add an identity.
+	 *
+	 * @param identity the identity
+	 * @note If the identity already exists, it is overriden
+	 */
+	inline void addIdentity(ServerIdentity identity) noexcept
+	{
+		m_identities.emplace(identity.name, std::move(identity));
+	}
+
+	/**
+	 * Get an identity, if not found, the default one is used.
+	 *
+	 * @param name the identity name
+	 * @return the identity or default one
+	 */
+	inline ServerIdentity findIdentity(const std::string &name) const noexcept
+	{
+		auto it = m_identities.find(name);
+
+		return it == m_identities.end() ? ServerIdentity() : it->second;
+	}
+
+	/*
+	 * Server management
+	 * ----------------------------------------------------------
+	 *
+	 * Functions to get or create new servers.
+	 *
+	 * Servers that are added to this instance are automatically polled when run() is called.
+	 */
+
+	/**
+	 * Check if a server exists.
+	 *
+	 * @param name the name
+	 * @return true if exists
+	 */
+	inline bool hasServer(const std::string &name) const noexcept
+	{
+		return m_servers.count(name) > 0;
+	}
+
+	/**
+	 * Add a new server to the application.
+	 *
+	 * @pre hasServer must return false
+	 * @param sv the server
+	 */
+	void addServer(std::shared_ptr<Server> sv) noexcept;
+
+	/**
+	 * Get a server or empty one if not found
+	 *
+	 * @param name the server name
+	 * @return the server or empty one if not found
+	 */
+	std::shared_ptr<Server> getServer(const std::string &name) const noexcept;
+
+	/**
+	 * Find a server by name.
+	 *
+	 * @param name the server name
+	 * @return the server
+	 * @throw std::out_of_range if the server does not exist
+	 */
+	std::shared_ptr<Server> requireServer(const std::string &name) const;
+
+	/**
+	 * Get the map of loaded servers.
+	 *
+	 * @return the servers
+	 */
+	inline const Servers &servers() const noexcept
+	{
+		return m_servers;
+	}
+
+	/**
+	 * Remove a server from the irccd instance.
+	 *
+	 * The server if any, will be disconnected.
+	 *
+	 * @param name the server name
+	 */
+	void removeServer(const std::string &name);
+
+	/**
+	 * Remove all servers.
+	 *
+	 * All servers will be disconnected.
+	 */
+	void clearServers() noexcept;
+
+	/*
+	 * Transport management
+	 * ----------------------------------------------------------
+	 *
+	 * Functions for adding new transport servers.
+	 */
+
+	/**
+	 * Add a transport server.
+	 *
+	 * @param ts the transport server
+	 */
+	void addTransport(std::shared_ptr<TransportServer> ts);
+
+	/*
+	 * Plugin management
+	 * ----------------------------------------------------------
+	 *
+	 * Functions for loading JavaScript plugins.
+	 */
+
+#if defined(WITH_JS)
+	/**
+	 * Check if a plugin is loaded.
+	 *
+	 * @param name the plugin id
+	 * @return true if has plugin
+	 */
+	inline bool hasPlugin(const std::string &name) const noexcept
+	{
+		return m_plugins.count(name) > 0;
+	}
+
+	/**
+	 * Get a plugin or empty one if not found.
+	 *
+	 * @param name the plugin id
+	 * @return the plugin or empty one if not found
+	 */
+	std::shared_ptr<Plugin> getPlugin(const std::string &name) const noexcept;
+
+	/**
+	 * Find a plugin.
+	 *
+	 * @param name the plugin id
+	 * @return the plugin
+	 * @throws std::out_of_range if not found
+	 */
+	std::shared_ptr<Plugin> requirePlugin(const std::string &name) const;
+
+	/**
+	 * Add plugin configuration for the specified plugin.
+	 *
+	 * @param name
+	 * @param config
+	 */
+	inline void addPluginConfig(std::string name, PluginConfig config)
+	{
+		m_pluginConf.emplace(std::move(name), std::move(config));
+	}
+
+	/**
+	 * Add a loaded plugin.
+	 *
+	 * Plugins signals will be connected to the irccd main loop. The onLoad function will also be called and the
+	 * plugin is not added on errors.
+	 *
+	 * @pre plugin must not be empty
+	 * @param plugin the plugin
+	 */
+	void addPlugin(std::shared_ptr<Plugin> plugin);
+
+	/**
+	 * Load a plugin by path or by searching through directories.
+	 *
+	 * TODO: Move this somewhere else (e.g. Plugin::find).
+	 *
+	 * @param source the path or the plugin id to search
+	 * @param find set to true for searching by id
+	 */
+	void loadPlugin(std::string name, const std::string &source, bool find);
+
+	/**
+	 * Unload a plugin and remove it.
+	 *
+	 * @param name the plugin id
+	 */
+	void unloadPlugin(const std::string &name);
+
+	/**
+	 * Reload a plugin by calling onReload.
+	 *
+	 * @param name the plugin name
+	 * @throw std::exception on failures
+	 */
+	void reloadPlugin(const std::string &name);
+
+	/**
+	 * Get the map of plugins.
+	 *
+	 * @return the map of plugins
+	 */
+	inline const Plugins &plugins() const noexcept
+	{
+		return m_plugins;
+	}
+
+#endif // !WITH_JS
+
+	/*
+	 * Rule management
+	 * ----------------------------------------------------------
+	 *
+	 * Functions for adding, creating new rules that are used to filter IRC events before being processed
+	 * by JavaScript plugins.
+	 */
+
+	/**
+	 * Append a rule.
+	 *
+	 * @param rule the rule to append
+	 */
+	inline void addRule(Rule rule)
+	{
+		m_rules.push_back(std::move(rule));
+	}
+
+	/**
+	 * Insert a new rule at the specified position.
+	 *
+	 * @param rule the rule
+	 * @param position the position
+	 */
+	inline void insertRule(Rule rule, unsigned position)
+	{
+		assert(position <= m_rules.size());
+
+		m_rules.insert(m_rules.begin() + position, std::move(rule));
+	}
+
+	/**
+	 * Get the list of rules.
+	 *
+	 * @return the list of rules
+	 */
+	inline const std::vector<Rule> &rules() const noexcept
+	{
+		return m_rules;
+	}
+
+	/**
+	 * Remove a new rule from the specified position.
+	 *
+	 * @param rule the rule
+	 * @param position the position
+	 */
+	inline void removeRule(unsigned position)
+	{
+		assert(position < m_rules.size());
+
+		m_rules.erase(m_rules.begin() + position);
+	}
+
+	/**
+	 * Loop forever by calling poll() and dispatch() indefinitely.
+	 */
+	void run();
+
+	/**
+	 * Poll the next events without blocking (250 ms max).
+	 */
+	void poll();
+
+	/**
+	 * Dispatch the pending events, usually after calling poll().
+	 */
+	void dispatch();
+
+	/**
+	 * Request to stop, usually from a signal.
+	 */
+	void stop();
+};
+
+} // !irccd
+
+#endif // !_IRCCD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/irccdctl.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,503 @@
+/*
+ * irccdctl.cpp -- main irccdctl class
+ *
+ * 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 <cassert>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#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 "irccdctl.h"
+
+namespace irccd {
+
+using namespace net;
+using namespace net::address;
+using namespace net::option;
+using namespace net::protocol;
+
+using namespace std::placeholders;
+using namespace std::chrono_literals;
+
+/*
+ * Config file format
+ * ------------------------------------------------------------------
+ *
+ * [connect]
+ * type = "ip | unix"
+ *
+ * # if ip
+ * host = ""
+ * port = number
+ * domain = "ipv4 | ipv6", default: ipv4
+ *
+ * # if unix
+ * path = ""
+ *
+ * [alias]
+ * name = replacement
+ */
+
+/*
+ * Initialize a connection from the configuration file
+ * ------------------------------------------------------------------
+ */
+
+void Irccdctl::usage() const
+{
+	log::warning() << "usage: " << sys::programName() << " [options...] <command> [command-options...] [command-args...]\n\n";
+	log::warning() << "General options:\n";
+	log::warning() << "\tc, --config file\tspecify the configuration file\n";
+	log::warning() << "\t--help\t\t\tshow this help\n";
+	log::warning() << "\t-t, --type type\t\tspecify connection type\n";
+	log::warning() << "\t-v, --verbose\t\tbe verbose\n\n";
+	log::warning() << "Available options for type ip and ipv6 (-t, --type):\n";
+	log::warning() << "\t-h, --host address\tconnect to the specified address\n";
+	log::warning() << "\t-p, --port port\t\tuse the specified port number\n\n";
+	log::warning() << "Available options for type unix (-t, --type):\n";
+	log::warning() << "\t-P, --path file\t\tconnect to the specified socket file\n\n";
+	log::warning() << "General commands:\n";
+	log::warning() << "\thelp\t\t\tShow an help topic\n";
+	log::warning() << "\twatch\t\t\tStart listening to irccd\n\n";
+	log::warning() << "Plugin management:\n";
+	log::warning() << "\tplugin-info\t\tGet plugin information\n";
+	log::warning() << "\tplugin-list\t\tList all loaded plugins\n";
+	log::warning() << "\tplugin-load\t\tLoad a plugin\n";
+	log::warning() << "\tplugin-reload\t\tReload a plugin\n";
+	log::warning() << "\tplugin-unload\t\tUnload a plugin\n\n";
+	log::warning() << "Server management:\n";
+	log::warning() << "\tserver-cmode\t\tChange a channel mode\n";
+	log::warning() << "\tserver-cnotice\t\tSend a channel notice\n";
+	log::warning() << "\tserver-connect\t\tConnect to a server\n";
+	log::warning() << "\tserver-disconnect\tDisconnect from a server\n";
+	log::warning() << "\tserver-info\t\tGet server information\n";
+	log::warning() << "\tserver-invite\t\tInvite someone to a channel\n";
+	log::warning() << "\tserver-join\t\tJoin a channel\n";
+	log::warning() << "\tserver-kick\t\tKick someone from a channel\n";
+	log::warning() << "\tserver-list\t\tList all servers\n";
+	log::warning() << "\tserver-me\t\tSend a CTCP Action (same as /me)\n";
+	log::warning() << "\tserver-message\t\tSend a message to someone or a channel\n";
+	log::warning() << "\tserver-mode\t\tChange a user mode\n";
+	log::warning() << "\tserver-notice\t\tSend a private notice\n";
+	log::warning() << "\tserver-nick\t\tChange your nickname\n";
+	log::warning() << "\tserver-part\t\tLeave a channel\n";
+	log::warning() << "\tserver-reconnect\tReconnect one or all servers\n";
+	log::warning() << "\tserver-topic\t\tChange a channel topic\n";
+	log::warning() << "\nFor more information on a command, type " << sys::programName() << " help <command>" << std::endl;
+	std::exit(1);
+}
+
+void Irccdctl::readConnectIp(const ini::Section &sc)
+{
+	ini::Section::const_iterator it;
+
+	/* host */
+	std::string host;
+
+	if ((it = sc.find("host")) == sc.end())
+		throw std::invalid_argument("missing host parameter");
+
+	host = it->value();
+
+	/* port */
+	int port;
+
+	if ((it = sc.find("port")) == sc.end())
+		throw std::invalid_argument("missing port parameter");
+
+	try {
+		port = std::stoi(it->value());
+	} catch (...) {
+		throw std::invalid_argument("invalid port number: " + it->value());
+	}
+
+	/* domain */
+	Ip::Type domain{Ip::v4};
+
+	if ((it = sc.find("domain")) != sc.end()) {
+		if (it->value() == "ipv6")
+			domain = Ip::v6;
+		else if (it->value() == "ipv4")
+			domain = Ip::v4;
+		else
+			throw std::invalid_argument("invalid domain: " + it->value());
+	}
+
+	m_connection = std::make_unique<ConnectionBase<Ip>>(Ip{host, port, domain});
+}
+
+void Irccdctl::readConnectUnix(const ini::Section &sc)
+{
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+	auto it = sc.find("path");
+
+	if (it == sc.end())
+		throw std::invalid_argument("missing path parameter");
+
+	m_connection = std::make_unique<ConnectionBase<Local>>(Local{it->value(), false});
+#else
+	(void)sc;
+
+	throw std::invalid_argument("unix connection not supported on Windows");
+#endif
+}
+
+void Irccdctl::readConnect(const ini::Section &sc)
+{
+	auto it = sc.find("type");
+
+	if (it == sc.end())
+		throw std::invalid_argument("missing type parameter");
+
+	if (it->value() == "ip")
+		readConnectIp(sc);
+	else if (it->value() == "unix")
+		readConnectUnix(sc);
+	else
+		throw std::invalid_argument("invalid type given: " + it->value());
+}
+
+void Irccdctl::readGeneral(const ini::Section &sc)
+{
+	auto verbose = sc.find("verbose");
+
+	if (verbose != sc.end())
+		log::setVerbose(util::isBoolean(verbose->value()));
+}
+
+void Irccdctl::readAliases(const ini::Section &sc)
+{
+	for (const ini::Option &option : sc) {
+		/* This is the alias name */
+		Alias alias(option.key());
+
+		if (m_commands.count(option.key()) > 0)
+			throw std::invalid_argument("there is already a command named " + option.key());
+
+		/* Iterate over the list of commands to execute for this alias */
+		for (const std::string &repl : option) {
+			/* This is the alias split string */
+			std::vector<std::string> list = util::split(repl, " \t");
+
+			if (list.size() < 1)
+				throw std::invalid_argument("alias require at least one argument");
+
+			/* First argument is the command/alias to execute */
+			std::string command = list[0];
+
+			/* This is the alias arguments */
+			std::vector<AliasArg> args;
+
+			for (auto it = list.begin() + 1; it != list.end(); ++it)
+				args.push_back(std::move(*it));
+
+			alias.push_back({std::move(command), std::move(args)});
+		}
+
+		/* Show for debugging purpose */
+		log::debug() << "alias " << option.key() << ":" << std::endl;
+
+		for (const auto &cmd : alias) {
+			log::debug() << "  " << cmd.command() << " ";
+			log::debug() << util::join(cmd.args().begin(), cmd.args().end(), ' ') << std::endl;
+		}
+
+		m_aliases.emplace(option.key(), std::move(alias));
+	}
+}
+
+void Irccdctl::read(const std::string &path, const parser::Result &options)
+{
+	ini::Document doc(ini::File{path});
+	ini::Document::const_iterator it = doc.find("connect");
+
+	/* Do not try to read [connect] if specified at command line */
+	if (it != doc.end() && options.count("-t") == 0 && options.count("--type") == 0)
+		readConnect(*it);
+	if ((it = doc.find("general")) != doc.end())
+		readGeneral(*it);
+	if ((it = doc.find("alias")) != doc.end())
+		readAliases(*it);
+}
+
+/*
+ * Initialize a connection from the command line
+ * ------------------------------------------------------------------
+ */
+
+void Irccdctl::parseConnectIp(const parser::Result &options, bool ipv6)
+{
+	parser::Result::const_iterator it;
+
+	/* host (-h or --host) */
+	std::string host;
+
+	if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end())
+		throw std::invalid_argument("missing host argument (-h or --host)");
+
+	host = it->second;
+
+	/* port (-p or --port) */
+	int port;
+
+	if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end())
+		throw std::invalid_argument("missing port argument (-p or --port)");
+
+	try {
+		port = std::stoi(it->second);
+	} catch (...) {
+		throw std::invalid_argument("invalid port number: " + it->second);
+	}
+
+	m_connection =  std::make_unique<ConnectionBase<Ip>>(Ip{host, port, (ipv6) ? Ip::v6 : Ip::v4});
+}
+
+void Irccdctl::parseConnectUnix(const parser::Result &options)
+{
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+	parser::Result::const_iterator it;
+
+	if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end())
+		throw std::invalid_argument("missing path parameter (-P or --path)");
+
+	m_connection = std::make_unique<ConnectionBase<Local>>(Local{it->second, false});
+#else
+	(void)options;
+
+	throw std::invalid_argument("unix connection not supported on Windows");
+#endif
+}
+
+void Irccdctl::parseConnect(const parser::Result &options)
+{
+	assert(options.count("-t") > 0 || options.count("--type") > 0);
+
+	auto it = options.find("-t");
+
+	if (it == options.end())
+		it = options.find("--type");
+	if (it->second == "ip" || it->second == "ipv6")
+		return parseConnectIp(options, it->second == "ipv6");
+	if (it->second == "unix")
+		return parseConnectUnix(options);
+
+	throw std::invalid_argument("invalid type given: " + it->second);
+}
+
+parser::Result Irccdctl::parse(int &argc, char **&argv) const
+{
+	/* 1. Parse command line options */
+	parser::Options def{
+		{ "-c",		true	},
+		{ "--config",	true	},
+		{ "-h",		true	},
+		{ "--help",	false	},
+		{ "--host",	true	},
+		{ "-p",		true	},
+		{ "--port",	true	},
+		{ "-P",		true	},
+		{ "--path",	true	},
+		{ "-t",		true	},
+		{ "--type",	true	},
+		{ "-v",		false	},
+		{ "--verbose",	false	}
+	};
+
+	parser::Result result;
+
+	try {
+		result = parser::read(argc, argv, def);
+
+		if (result.count("--help") != 0)
+			usage();
+			// NOTREACHED
+
+		if (result.count("-v") != 0 || result.count("--verbose") != 0)
+			log::setVerbose(true);
+	} catch (const std::exception &ex) {
+		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
+		usage();
+	}
+
+	return result;
+}
+
+void Irccdctl::exec(const RemoteCommand &cmd, std::vector<std::string> args)
+{
+	
+}
+
+void Irccdctl::exec(const Alias &alias, std::vector<std::string> argsCopy)
+{
+	for (const AliasCommand &cmd : alias) {
+		std::vector<std::string> args(argsCopy);
+		std::vector<std::string> cmdArgs;
+		std::vector<std::string>::size_type toremove = 0;
+
+		/* 1. Append command name before */
+		cmdArgs.push_back(cmd.command());
+
+		for (const AliasArg &arg : cmd.args()) {
+			if (arg.isPlaceholder()) {
+				if (args.size() < arg.index() + 1)
+					throw std::invalid_argument("missing argument for placeholder %" + std::to_string(arg.index()));
+
+				cmdArgs.push_back(args[arg.index()]);
+
+				if (arg.index() + 1 > toremove)
+					toremove = arg.index() + 1;
+			} else {
+				cmdArgs.push_back(arg.value());
+			}
+		}
+
+		assert(toremove <= args.size());
+
+		/* 2. Remove the arguments that been placed in placeholders */
+		args.erase(args.begin(), args.begin() + toremove);
+
+		/* 3. Now append the rest of arguments */
+		std::copy(args.begin(), args.end(), std::back_inserter(cmdArgs));
+
+		/* 4. Finally try to execute */
+		exec(cmdArgs);
+	}
+}
+
+void Irccdctl::exec(std::vector<std::string> args)
+{
+	assert(args.size() > 0);
+
+	auto name = args[0];
+	auto alias = m_aliases.find(name);
+
+	/* Remove name */
+	args.erase(args.begin());
+
+	if (alias != m_aliases.end()) {
+		exec(alias->second, args);
+	} else {
+		auto cmd = m_commands.find(name);
+
+		if (cmd != m_commands.end())
+			exec(*cmd->second, args);
+		else
+			throw std::invalid_argument("no alias or command named " + name);
+	}
+}
+
+void Irccdctl::connect()
+{
+	log::info() << sys::programName() << ": connecting to irccd..." << std::endl;
+
+	/* Try to connect */
+	m_connection->connect(30000);
+
+	/* Get irccd information */
+	json::Value object = m_connection->next(30000);
+
+	if (!object.contains("program") || object.at("program").toString() != "irccd")
+		throw std::runtime_error("not an irccd server");
+
+	/* Get values */
+	m_major = object.at("major").toInt();
+	m_minor = object.at("minor").toInt();
+	m_patch = object.at("patch").toInt();
+	m_javascript = object.valueOr("javascript", json::Type::Boolean, false).toBool();
+	m_ssl = object.valueOr("ssl", json::Type::Boolean, false).toBool();
+
+	log::info() << std::boolalpha;
+	log::info() << sys::programName() << ": connected to irccd " << m_major << "." << m_minor << "." << m_patch << std::endl;
+	log::info() << sys::programName() << ": javascript: " << m_javascript << ", ssl supported: " << m_ssl << std::endl;
+}
+
+void Irccdctl::run(int argc, char **argv)
+{
+	/* 1. Read command line arguments */
+	parser::Result result = parse(argc, argv);
+
+	/*
+	 * 2. Open optional config by command line or by searching it
+	 *
+	 * The connection to irccd is searched in the following order :
+	 *
+	 * 1. From the command line if specified
+	 * 2. From the configuration file specified by -c
+	 * 3. From the configuration file searched through directories
+	 */
+	try {
+		if (result.count("-t") > 0 || result.count("--type") > 0)
+			parseConnect(result);
+
+		auto it = result.find("-c");
+
+		if (it != result.end() || (it = result.find("--config")) != result.end()) {
+			read(it->second, result);
+		} else {
+			for (const std::string &dir : path::list(path::PathConfig)) {
+				std::string path = dir + "irccdctl.conf";
+
+				if (fs::exists(path))
+					read(path, result);
+			}
+		}
+	} catch (const std::exception &ex) {
+		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
+		std::exit(1);
+	}
+
+	if (argc <= 0) {
+		usage();
+		// NOTREACHED
+	}
+
+	/* help does not require connection */
+	if (std::strcmp(argv[0], "help") != 0) {
+		if (!m_connection) {
+			log::warning() << sys::programName() << ": no connection specified" << std::endl;
+			std::exit(1);
+		}
+
+		connect();
+	}
+
+	/* Build a vector of arguments */
+	std::vector<std::string> args;
+
+	for (int i = 0; i < argc; ++i)
+		args.push_back(argv[i]);
+
+	exec(args);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/irccdctl.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,112 @@
+/*
+ * irccdctl.h -- main irccdctl class
+ *
+ * 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_H_
+#define _IRCCDCTL_H_
+
+#include <cassert>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "private/connection.h"
+
+#include "alias.h"
+#include "application.h"
+#include "options.h"
+
+namespace irccd {
+
+class Command;
+
+namespace ini {
+
+class Section;
+
+} // !ini
+
+class Irccdctl : public Application {
+private:
+	/* Irccd's information */
+	unsigned short m_major{0};
+	unsigned short m_minor{0};
+	unsigned short m_patch{0};
+
+	/* Irccd's compilation option */
+	bool m_javascript{true};
+	bool m_ssl{true};
+
+	std::unique_ptr<Connection> m_connection;
+	std::unordered_map<std::string, Alias> m_aliases;
+
+	void usage() const;
+
+	void readConnectIp(const ini::Section &sc);
+	void readConnectUnix(const ini::Section &sc);
+	void readConnect(const ini::Section &sc);
+	void readGeneral(const ini::Section &sc);
+	void readAliases(const ini::Section &sc);
+	void read(const std::string &path, const parser::Result &options);
+
+	void parseConnectIp(const parser::Result &options, bool ipv6);
+	void parseConnectUnix(const parser::Result &options);
+	void parseConnect(const parser::Result &options);
+	parser::Result parse(int &argc, char **&argv) const;
+
+	void connect();
+
+public:
+	/**
+	 * Execute the given command and wait for its result.
+	 *
+	 * @param cmd the command
+	 * @param args the arguments
+	 */
+	void exec(const RemoteCommand &cmd, std::vector<std::string> args);
+
+	/**
+	 * Execute the given alias.
+	 *
+	 * @param alias the alias
+	 * @param args the arguments
+	 */
+	void exec(const Alias &alias, std::vector<std::string> args);
+
+	/**
+	 * Resolve the command line arguments.
+	 *
+	 * @param args the main arguments
+	 */
+	void exec(std::vector<std::string> args);
+
+	/**
+	 * Get the connection.
+	 *
+	 * @return the connection
+	 */
+	inline Connection &connection() noexcept
+	{
+		return *m_connection;
+	}
+
+	void run(int argc, char **argv);
+};
+
+} // !irccd
+
+#endif // !_IRCCDCTL_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/CMakeSources.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,31 @@
+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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/directory.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,389 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/directory.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &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	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,133 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/elapsed-timer.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &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	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,628 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/file.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,184 @@
+/*
+ * 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_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/irccd.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,85 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/irccd.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,46 @@
+/*
+ * 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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/js.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,105 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/js.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,2183 @@
+/*
+ * 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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/logger.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,98 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/logger.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx);
+
+} // !irccd
+
+#endif // !_IRCCD_JS_LOGGER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/plugin.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,181 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/plugin.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx) noexcept;
+
+} // !irccd
+
+#endif // !_IRCCD_JS_TIMER_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/server.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,540 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/server.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,30 @@
+/*
+ * 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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/system.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,236 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/system.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx);
+
+} // !irccd
+
+#endif // !_IRCCD_JS_SYSTEM_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/timer.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,143 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/timer.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx) noexcept;
+
+} // !irccd
+
+#endif // !_IRCCD_JS_TIMER_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/unicode.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,143 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/unicode.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx);
+
+} // !irccd
+
+#endif // !_IRCCD_JS_UNICODE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/util.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,149 @@
+/*
+ * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/js/util.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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(js::Context &ctx);
+
+} // !irccd
+
+#endif // !_IRCCD_JS_UTIL_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/json.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,369 @@
+/*
+ * json.cpp -- C++14 JSON manipulation using jansson parser
+ *
+ * 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 <jansson.h>
+
+#include <sstream>
+
+#include "json.h"
+
+namespace irccd {
+
+namespace json {
+
+namespace {
+
+void readObject(Value &parent, json_t *object);
+void readArray(Value &parent, json_t *array);
+
+Value readValue(json_t *v)
+{
+	if (json_is_null(v))
+		return Value(nullptr);
+	if (json_is_string(v))
+		return Value(json_string_value(v));
+	if (json_is_real(v))
+		return Value(json_number_value(v));
+	if (json_is_integer(v))
+		return Value(static_cast<int>(json_integer_value(v)));
+	if (json_is_boolean(v))
+		return Value(json_boolean_value(v));
+	if (json_is_object(v)) {
+		Value object(Type::Object);
+
+		readObject(object, v);
+
+		return object;
+	}
+	if (json_is_array(v)) {
+		Value array(Type::Array);
+
+		readArray(array, v);
+
+		return array;
+	}
+
+	return Value();
+}
+
+void readObject(Value &parent, json_t *object)
+{
+	const char *key;
+	json_t *value;
+
+	json_object_foreach(object, key, value)
+		parent.insert(key, readValue(value));
+}
+
+void readArray(Value &parent, json_t *array)
+{
+	size_t index;
+	json_t *value;
+
+	json_array_foreach(array, index, value)
+		parent.append(readValue(value));
+}
+
+template <typename Func, typename... Args>
+Value convert(Func fn, Args&&... args)
+{
+	json_error_t error;
+	json_t *json = fn(std::forward<Args>(args)..., &error);
+
+	if (json == nullptr)
+		throw Error(error.text, error.source, error.line, error.column, error.position);
+
+	Value value;
+
+	if (json_is_object(json)) {
+		value = Value(Type::Object);
+		readObject(value, json);
+	} else {
+		value = Value(Type::Array);
+		readArray(value, json);
+	}
+
+	json_decref(json);
+
+	return value;
+}
+
+std::string indent(int param, int level)
+{
+	std::string str;
+
+	if (param < 0)
+		str = std::string(level, '\t');
+	else if (param > 0)
+		str = std::string(param * level, ' ');
+
+	return str;
+}
+
+} // !namespace
+
+void Value::copy(const Value &other)
+{
+	switch (other.m_type) {
+	case Type::Array:
+		new (&m_array) std::vector<Value>(other.m_array);
+		break;
+	case Type::Boolean:
+		m_boolean = other.m_boolean;
+		break;
+	case Type::Int:
+		m_integer = other.m_integer;
+		break;
+	case Type::Object:
+		new (&m_object) std::map<std::string, Value>(other.m_object);
+		break;
+	case Type::Real:
+		m_number = other.m_number;
+		break;
+	case Type::String:
+		new (&m_string) std::string(other.m_string);
+		break;
+	default:
+		break;
+	}
+
+	m_type = other.m_type;
+}
+
+void Value::move(Value &&other)
+{
+	switch (other.m_type) {
+	case Type::Array:
+		new (&m_array) std::vector<Value>(std::move(other.m_array));
+		break;
+	case Type::Boolean:
+		m_boolean = other.m_boolean;
+		break;
+	case Type::Int:
+		m_integer = other.m_integer;
+		break;
+	case Type::Object:
+		new (&m_object) std::map<std::string, Value>(std::move(other.m_object));
+		break;
+	case Type::Real:
+		m_number = other.m_number;
+		break;
+	case Type::String:
+		new (&m_string) std::string(std::move(other.m_string));
+		break;
+	default:
+		break;
+	}
+
+	m_type = other.m_type;
+}
+
+Value::Value(Type type)
+	: m_type(type)
+{
+	switch (m_type) {
+	case Type::Array:
+		new (&m_array) std::vector<Value>();
+		break;
+	case Type::Boolean:
+		m_boolean = false;
+		break;
+	case Type::Int:
+		m_integer = 0;
+		break;
+	case Type::Object:
+		new (&m_object) std::map<std::string, Value>();
+		break;
+	case Type::Real:
+		m_number = 0;
+		break;
+	case Type::String:
+		new (&m_string) std::string();
+		break;
+	default:
+		break;
+	}
+}
+
+Value::~Value()
+{
+	switch (m_type) {
+	case Type::Array:
+		m_array.~vector<Value>();
+		break;
+	case Type::Object:
+		m_object.~map<std::string, Value>();
+		break;
+	case Type::String:
+		m_string.~basic_string();
+		break;
+	default:
+		break;
+	}
+}
+
+bool Value::toBool() const noexcept
+{
+	if (m_type != Type::Boolean)
+		return false;
+
+	return m_boolean;
+}
+
+double Value::toReal() const noexcept
+{
+	if (m_type != Type::Real)
+		return 0;
+
+	return m_number;
+}
+
+int Value::toInt() const noexcept
+{
+	if (m_type != Type::Int)
+		return 0;
+
+	return m_integer;
+}
+
+std::string Value::toString(bool coerce) const noexcept
+{
+	std::string result;
+
+	if (m_type == Type::String)
+		result = m_string;
+	else if (coerce)
+		result = toJson();
+
+	return result;
+}
+
+Value::Value(const Buffer &buffer)
+{
+	*this = convert(json_loads, buffer.text.c_str(), 0);
+}
+
+Value::Value(const File &file)
+{
+	*this = convert(json_load_file, file.path.c_str(), 0);
+}
+
+std::string Value::toJson(int level, int current) const
+{
+	std::ostringstream oss;
+
+	switch (m_type) {
+	case Type::Array: {
+		oss << '[' << (level != 0 ? "\n" : "");
+
+		unsigned total = m_array.size();
+		unsigned i = 0;
+		for (const auto &v : m_array) {
+			oss << indent(level, current + 1) << v.toJson(level, current + 1);
+			oss << (++i < total ? "," : "");
+			oss << (level != 0 ? "\n" : "");
+		}
+
+		oss << (level != 0 ? indent(level, current) : "") << ']';
+		break;
+	}
+	case Type::Boolean:
+		oss << (m_boolean ? "true" : "false");
+		break;
+	case Type::Int:
+		oss << m_integer;
+		break;
+	case Type::Null:
+		oss << "null";
+		break;
+	case Type::Object: {
+		oss << '{' << (level != 0 ? "\n" : "");
+
+		unsigned total = m_object.size();
+		unsigned i = 0;
+		for (const auto &pair : m_object) {
+			oss << indent(level, current + 1);
+
+			/* Key and : */
+			oss << "\"" << pair.first << "\":" << (level != 0 ? " " : "");
+
+			/* Value */
+			oss << pair.second.toJson(level, current + 1);
+
+			/* Comma, new line if needed */
+			oss << (++i < total ? "," : "") << (level != 0 ? "\n" : "");
+		}
+
+		oss << (level != 0 ? indent(level, current) : "") << '}';
+		break;
+	}
+	case Type::Real:
+		oss << m_number;
+		break;
+	case Type::String:
+		oss << "\"" << escape(m_string) << "\"";
+		break;
+	default:
+		break;
+	}
+
+	return oss.str();
+}
+
+std::string escape(const std::string &value)
+{
+	std::string result;
+
+	for (auto it = value.begin(); it != value.end(); ++it) {
+		switch (*it) {
+		case '\\':
+			result += "\\\\";
+			break;
+		case '/':
+			result += "\\/";
+			break;
+		case '"':
+			result += "\\\"";
+			break;
+		case '\b':
+			result += "\\b";
+			break;
+		case '\f':
+			result += "\\f";
+			break;
+		case '\n':
+			result += "\\n";
+			break;
+		case '\r':
+			result += "\\r";
+			break;
+		case '\t':
+			result += "\\t";
+			break;
+		default:
+			result += *it;
+			break;
+		}
+	}
+
+	return result;
+}
+
+} // !json
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/json.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,1183 @@
+/*
+ * json.h -- C++14 JSON manipulation using jansson parser
+ *
+ * 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 _JSON_H_
+#define _JSON_H_
+
+/**
+ * @file json.h
+ * @brief Jansson C++14 wrapper
+ *
+ * These classes can be used to build or parse JSON documents using jansson library. It is designed to be safe
+ * and explicit. It does not implement implicit sharing like jansson so when you access (e.g. Value::toObject) values
+ * you get real copies, thus when you read big documents it can has a performance cost.
+ */
+
+#include <cassert>
+#include <exception>
+#include <initializer_list>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * Json namespace.
+ */
+namespace json {
+
+/**
+ * @enum Type
+ * @brief Type of Value.
+ */
+enum class Type {
+	Array,		//!< Value is an array []
+	Boolean,	//!< Value is boolean
+	Int,		//!< Value is integer
+	Null,		//!< Value is defined to null
+	Object,		//!< Value is object {}
+	Real,		//!< Value is float
+	String		//!< Value is unicode string
+};
+
+/**
+ * @class Error
+ * @brief Error description.
+ */
+class Error : public std::exception {
+private:
+	std::string m_text;
+	std::string m_source;
+	int m_line;
+	int m_column;
+	int m_position;
+
+public:
+	/**
+	 * Create the error.
+	 *
+	 * @param text the text message
+	 * @param source the source (e.g. file name)
+	 * @param line the line number
+	 * @param column the column number
+	 * @param position the position
+	 */
+	inline Error(std::string text, std::string source, int line, int column, int position) noexcept
+		: m_text(std::move(text))
+		, m_source(std::move(source))
+		, m_line(line)
+		, m_column(column)
+		, m_position(position)
+	{
+	}
+
+	/**
+	 * Get the error message.
+	 *
+	 * @return the text
+	 */
+	inline const std::string &text() const noexcept
+	{
+		return m_text;
+	}
+
+	/**
+	 * Get the source (e.g. a file name).
+	 *
+	 * @return the source
+	 */
+	inline const std::string &source() const noexcept
+	{
+		return m_source;
+	}
+
+	/**
+	 * 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 position.
+	 *
+	 * @return the position
+	 */
+	inline int position() const noexcept
+	{
+		return m_position;
+	}
+
+	/**
+	 * Get the error message.
+	 *
+	 * @return the message
+	 */
+	const char *what() const noexcept override
+	{
+		return m_text.c_str();
+	}
+};
+
+/**
+ * @class Buffer
+ * @brief Open JSON document from text.
+ */
+class Buffer {
+public:
+	std::string text;	//!< The JSON text
+};
+
+/**
+ * @class File
+ * @brief Open JSON document from a file.
+ */
+class File {
+public:
+	std::string path;	//!< The path to the file
+};
+
+/**
+ * @class Value
+ * @brief Generic JSON value wrapper.
+ */
+class Value {
+private:
+	Type m_type{Type::Null};
+
+	union {
+		double m_number;
+		bool m_boolean;
+		int m_integer;
+		std::string m_string;
+		std::vector<Value> m_array;
+		std::map<std::string, Value> m_object;
+	};
+
+	void copy(const Value &);
+	void move(Value &&);
+	std::string toJson(int indent, int current) const;
+
+	/**
+	 * @class BaseIterator
+	 * @brief This is the base class for iterator and const_iterator
+	 *
+	 * This iterator works for both arrays and objects. Because of that purpose, it is only available
+	 * as forward iterator.
+	 *
+	 * When iterator comes from an object, you can use key() otherwise you can use index().
+	 */
+	template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType>
+	class BaseIterator : public std::iterator<std::forward_iterator_tag, ValueType> {
+	private:
+		friend class Value;
+
+		ValueType &m_value;
+		ArrayIteratorType m_ita;
+		ObjectIteratorType m_itm;
+
+		inline void increment()
+		{
+			if (m_value.isObject())
+				m_itm++;
+			else
+				m_ita++;
+		}
+
+		BaseIterator(ValueType &value, ObjectIteratorType it)
+			: m_value(value)
+			, m_itm(it)
+		{
+		}
+
+		BaseIterator(ValueType &value, ArrayIteratorType it)
+			: m_value(value)
+			, m_ita(it)
+		{
+		}
+
+	public:
+		/**
+		 * Get the iterator key (for objects).
+		 *
+		 * @pre iterator must be dereferenceable
+		 * @pre iterator must come from object
+		 * @return the key
+		 */
+		inline const std::string &key() const noexcept
+		{
+			assert(m_value.isObject());
+			assert(m_itm != m_value.m_object.end());
+
+			return m_itm->first;
+		}
+
+		/**
+		 * Get the iterator position (for arrays).
+		 *
+		 * @pre iterator must be dereferenceable
+		 * @pre iterator must come from arrays
+		 * @return the index
+		 */
+		inline unsigned index() const noexcept
+		{
+			assert(m_value.isArray());
+			assert(m_ita != m_value.m_array.end());
+
+			return std::distance(m_value.m_array.begin(), m_ita);
+		}
+
+		/**
+		 * Dereference the iterator.
+		 *
+		 * @pre iterator be dereferenceable
+		 * @return the value
+		 */
+		inline ValueType &operator*() noexcept
+		{
+			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
+			       (m_value.isObject() && m_itm != m_value.m_object.end()));
+
+			return (m_value.m_type == Type::Object) ? m_itm->second : *m_ita;
+		}
+
+		/**
+		 * Dereference the iterator as a pointer.
+		 *
+		 * @pre iterator must be dereferenceable
+		 * @return the value
+		 */
+		inline ValueType *operator->() noexcept
+		{
+			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
+			       (m_value.isObject() && m_itm != m_value.m_object.end()));
+
+			return (m_value.m_type == Type::Object) ? &m_itm->second : &(*m_ita);
+		}
+
+		/**
+		 * Increment the iterator. (Prefix version).
+		 *
+		 * @pre iterator must be dereferenceable
+		 * @return *this;
+		 */
+		inline BaseIterator &operator++() noexcept
+		{
+			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
+			       (m_value.isObject() && m_itm != m_value.m_object.end()));
+
+			increment();
+
+			return *this;
+		}
+
+		/**
+		 * Increment the iterator. (Postfix version).
+		 *
+		 * @pre iterator must be dereferenceable
+		 * @return *this;
+		 */
+		inline BaseIterator &operator++(int) noexcept
+		{
+			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
+			       (m_value.isObject() && m_itm != m_value.m_object.end()));
+
+			increment();
+
+			return *this;
+		}
+
+		/**
+		 * Compare two iterators.
+		 *
+		 * @param it1 the first iterator
+		 * @param it2 the second iterator
+		 * @return true if they are same
+		 */
+		bool operator==(const BaseIterator &it) const noexcept
+		{
+			if (m_value.isObject() && it.m_value.isObject())
+				return m_itm == it.m_itm;
+			if (m_value.isArray() && it.m_value.isArray())
+				return m_ita == it.m_ita;
+
+			return false;
+		}
+
+		/**
+		 * Test if the iterator is different.
+		 *
+		 * @param it the iterator
+		 * @return true if they are different
+		 */
+		inline bool operator!=(const BaseIterator &it) const noexcept
+		{
+			return !(*this == it);
+		}
+	};
+
+public:
+	/**
+	 * Forward iterator.
+	 */
+	using iterator = BaseIterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>;
+
+	/**
+	 * Const forward iterator.
+	 */
+	using const_iterator = BaseIterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>;
+
+	/**
+	 * Construct a null value.
+	 */
+	inline Value()
+	{
+	}
+
+	/**
+	 * Create a value with a specified type, this is usually only needed when you want to create an object or
+	 * an array.
+	 *
+	 * For any other types, initialize with sane default value.
+	 *
+	 * @param type the type
+	 */
+	Value(Type type);
+
+	/**
+	 * Construct a null value.
+	 */
+	inline Value(std::nullptr_t) noexcept
+		: m_type(Type::Null)
+	{
+	}
+
+	/**
+	 * Construct a boolean value.
+	 *
+	 * @param value the boolean value
+	 */
+	inline Value(bool value) noexcept
+		: m_type(Type::Boolean)
+		, m_boolean(value)
+	{
+	}
+
+	/**
+	 * Create value from integer.
+	 *
+	 * @param value the value
+	 */
+	inline Value(int value) noexcept
+		: m_type(Type::Int)
+		, m_integer(value)
+	{
+	}
+
+	/**
+	 * Construct a value from a C-string.
+	 *
+	 * @param value the C-string
+	 */
+	inline Value(const char *value)
+		: m_type(Type::String)
+	{
+		new (&m_string) std::string(value ? value : "");
+	}
+
+	/**
+	 * Construct a number value.
+	 *
+	 * @param value the real value
+	 */
+	inline Value(double value) noexcept
+		: m_type(Type::Real)
+		, m_number(value)
+	{
+	}
+
+	/**
+	 * Construct a string value.
+	 *
+	 * @param value the string
+	 */
+	inline Value(std::string value) noexcept
+		: m_type(Type::String)
+	{
+		new (&m_string) std::string(std::move(value));
+	}
+
+	/**
+	 * Create an object from a map.
+	 *
+	 * @param values the values
+	 * @see fromObject
+	 */
+	inline Value(std::map<std::string, Value> values)
+		: Value(Type::Object)
+	{
+		for (const auto &pair : values)
+			insert(pair.first, pair.second);
+	}
+
+	/**
+	 * Create an array from a vector.
+	 *
+	 * @param values the values
+	 * @see fromArray
+	 */
+	inline Value(std::vector<Value> values)
+		: Value(Type::Array)
+	{
+		for (Value value : values)
+			append(std::move(value));
+	}
+
+	/**
+	 * Construct a value from a buffer.
+	 *
+	 * @param buffer the text
+	 * @throw Error on errors
+	 */
+	Value(const Buffer &buffer);
+
+	/**
+	 * Construct a value from a file.
+	 *
+	 * @param file the file
+	 * @throw Error on errors
+	 */
+	Value(const File &file);
+
+	/**
+	 * Move constructor.
+	 *
+	 * @param other the value to move from
+	 */
+	inline Value(Value &&other)
+	{
+		move(std::move(other));
+	}
+
+	/**
+	 * Copy constructor.
+	 *
+	 * @param other the value to copy from
+	 */
+	inline Value(const Value &other)
+	{
+		copy(other);
+	}
+
+	/**
+	 * Copy operator.
+	 *
+	 * @param other the value to copy from
+	 * @return *this
+	 */
+	inline Value &operator=(const Value &other)
+	{
+		copy(other);
+
+		return *this;
+	}
+
+	/**
+	 * Move operator.
+	 *
+	 * @param other the value to move from
+	 */
+	inline Value &operator=(Value &&other)
+	{
+		move(std::move(other));
+
+		return *this;
+	}
+
+	/**
+	 * Destructor.
+	 */
+	~Value();
+
+	/**
+	 * Get an iterator to the beginning.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline iterator begin() noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? iterator(*this, m_object.begin()) : iterator(*this, m_array.begin());
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline const_iterator begin() const noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? const_iterator(*this, m_object.begin()) : const_iterator(*this, m_array.begin());
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline const_iterator cbegin() const noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? const_iterator(*this, m_object.cbegin()) : const_iterator(*this, m_array.cbegin());
+	}
+
+	/**
+	 * Get an iterator to the end.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline iterator end() noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? iterator(*this, m_object.end()) : iterator(*this, m_array.end());
+	}
+
+	/**
+	 * Get an iterator to the end.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline const_iterator end() const noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? const_iterator(*this, m_object.end()) : const_iterator(*this, m_array.end());
+	}
+
+	/**
+	 * Get an iterator to the end.
+	 *
+	 * @pre must be an array or object
+	 * @return the iterator
+	 */
+	inline const_iterator cend() const noexcept
+	{
+		assert(isArray() || isObject());
+
+		return m_type == Type::Object ? const_iterator(*this, m_object.cend()) : const_iterator(*this, m_array.cend());
+	}
+
+	/**
+	 * Get the value type.
+	 *
+	 * @return the type
+	 */
+	inline Type typeOf() const noexcept
+	{
+		return m_type;
+	}
+
+	/**
+	 * Get the value as boolean.
+	 *
+	 * @return the value or false if not a boolean
+	 */
+	bool toBool() const noexcept;
+
+	/**
+	 * Get the value as integer.
+	 *
+	 * @return the value or 0 if not a integer
+	 */
+	int toInt() const noexcept;
+
+	/**
+	 * Get the value as real.
+	 *
+	 * @return the value or 0 if not a real
+	 */
+	double toReal() const noexcept;
+
+	/**
+	 * Get the value as string.
+	 *
+	 * @param coerce set to true to coerce the value if not a string
+	 * @return the value or empty string if not a string
+	 */
+	std::string toString(bool coerce = false) const noexcept;
+
+	/**
+	 * Check if the value is boolean type.
+	 *
+	 * @return true if boolean
+	 */
+	inline bool isBool() const noexcept
+	{
+		return m_type == Type::Boolean;
+	}
+
+	/**
+	 * Check if the value is integer type.
+	 *
+	 * @return true if integer
+	 */
+	inline bool isInt() const noexcept
+	{
+		return m_type == Type::Int;
+	}
+
+	/**
+	 * Check if the value is object type.
+	 *
+	 * @return true if object
+	 */
+	inline bool isObject() const noexcept
+	{
+		return m_type == Type::Object;
+	}
+
+	/**
+	 * Check if the value is array type.
+	 *
+	 * @return true if array
+	 */
+	inline bool isArray() const noexcept
+	{
+		return m_type == Type::Array;
+	}
+
+	/**
+	 * Check if the value is integer or real type.
+	 *
+	 * @return true if integer or real
+	 * @see toInt
+	 * @see toReal
+	 */
+	inline bool isNumber() const noexcept
+	{
+		return m_type == Type::Real || m_type == Type::Int;
+	}
+
+	/**
+	 * Check if the value is real type.
+	 *
+	 * @return true if real
+	 */
+	inline bool isReal() const noexcept
+	{
+		return m_type == Type::Real;
+	}
+
+	/**
+	 * Check if the value is null type.
+	 *
+	 * @return true if null
+	 */
+	inline bool isNull() const noexcept
+	{
+		return m_type == Type::Null;
+	}
+
+	/**
+	 * Check if the value is string type.
+	 *
+	 * @return true if string
+	 */
+	inline bool isString() const noexcept
+	{
+		return m_type == Type::String;
+	}
+
+	/**
+	 * Get the array or object size.
+	 *
+	 * @pre must be an array or object
+	 * @return the size
+	 */
+	inline unsigned size() const noexcept
+	{
+		assert(isArray() || isObject());
+
+		if (m_type == Type::Object)
+			return m_object.size();
+
+		return m_array.size();
+	}
+
+	/**
+	 * Remove all the values.
+	 *
+	 * @pre must be an array or an object
+	 */
+	inline void clear() noexcept
+	{
+		assert(isArray() || isObject());
+
+		if (m_type == Type::Array)
+			m_array.clear();
+		else
+			m_object.clear();
+	}
+
+	/*
+	 * Array functions
+	 * ----------------------------------------------------------
+	 */
+
+	/**
+	 * Get the value at the specified position or the defaultValue if position is out of bounds.
+	 *
+	 * @param position the position
+	 * @param defaultValue the value replacement
+	 * @return the value or defaultValue
+	 */
+	template <typename DefaultValue>
+	inline Value valueOr(unsigned position, DefaultValue &&defaultValue) const
+	{
+		if (m_type != Type::Array || position >= m_array.size())
+			return defaultValue;
+
+		return m_array[position];
+	}
+
+	/**
+	 * Overloaded function with type check.
+	 *
+	 * @param position the position
+	 * @param type the requested type
+	 * @param defaultValue the value replacement
+	 * @return the value or defaultValue
+	 */
+	template <typename DefaultValue>
+	inline Value valueOr(unsigned position, Type type, DefaultValue &&defaultValue) const
+	{
+		if (m_type != Type::Array || position >= m_array.size() || m_array[position].typeOf() != type)
+			return defaultValue;
+
+		return m_array[position];
+	}
+
+	/**
+	 * Get a value at the specified index.
+	 *
+	 * @pre must be an array
+	 * @param position the position
+	 * @return the value
+	 * @throw std::out_of_range if out of bounds
+	 */
+	inline const Value &at(unsigned position) const
+	{
+		assert(isArray());
+
+		return m_array.at(position);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array
+	 * @param position the position
+	 * @return the value
+	 * @throw std::out_of_range if out of bounds
+	 */
+	inline Value &at(unsigned position)
+	{
+		assert(isArray());
+
+		return m_array.at(position);
+	}
+
+	/**
+	 * Get a value at the specified index.
+	 *
+	 * @pre must be an array
+	 * @pre position must be valid
+	 * @param position the position
+	 * @return the value
+	 */
+	inline const Value &operator[](unsigned position) const
+	{
+		assert(isArray());
+		assert(position < m_array.size());
+
+		return m_array[position];
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array
+	 * @pre position must be valid
+	 * @param position the position
+	 * @return the value
+	 */
+	inline Value &operator[](unsigned position)
+	{
+		assert(isArray());
+		assert(position < m_array.size());
+
+		return m_array[position];
+	}
+
+	/**
+	 * Push a value to the beginning of the array.
+	 *
+	 * @pre must be an array
+	 * @param value the value to push
+	 */
+	inline void push(const Value &value)
+	{
+		assert(isArray());
+
+		m_array.insert(m_array.begin(), value);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array
+	 * @param value the value to push
+	 */
+	inline void push(Value &&value)
+	{
+		assert(isArray());
+
+		m_array.insert(m_array.begin(), std::move(value));
+	}
+
+	/**
+	 * Insert a value at the specified position.
+	 *
+	 * @pre must be an array
+	 * @pre position must be valid
+	 * @param position the position
+	 * @param value the value to push
+	 */
+	inline void insert(unsigned position, const Value &value)
+	{
+		assert(isArray());
+		assert(position <= m_array.size());
+
+		m_array.insert(m_array.begin() + position, value);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array
+	 * @pre position must be valid
+	 * @param position the position
+	 * @param value the value to push
+	 */
+	inline void insert(unsigned position, Value &&value)
+	{
+		assert(isArray());
+		assert(position <= m_array.size());
+
+		m_array.insert(m_array.begin() + position, std::move(value));
+	}
+
+	/**
+	 * Add a new value to the end.
+	 *
+	 * @pre must be an array
+	 * @param value the value to append
+	 */
+	inline void append(const Value &value)
+	{
+		assert(isArray());
+
+		m_array.push_back(value);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an array
+	 * @param value the value to append
+	 */
+	inline void append(Value &&value)
+	{
+		assert(isArray());
+
+		m_array.push_back(std::move(value));
+	}
+
+	/**
+	 * Remove a value at the specified position.
+	 *
+	 * @pre must be an array
+	 * @pre position must be valid
+	 * @param position the position
+	 */
+	inline void erase(unsigned position)
+	{
+		assert(isArray());
+		assert(position < m_array.size());
+
+		m_array.erase(m_array.begin() + position);
+	}
+
+	/*
+	 * Object functions
+	 * ----------------------------------------------------------
+	 */
+
+	/**
+	 * Get the value at the specified key or the defaultValue if key is absent.
+	 *
+	 * @param name the name
+	 * @param defaultValue the value replacement
+	 * @return the value or defaultValue
+	 */
+	template <typename DefaultValue>
+	Value valueOr(const std::string &name, DefaultValue &&defaultValue) const
+	{
+		if (m_type != Type::Object)
+			return defaultValue;
+
+		auto it = m_object.find(name);
+
+		if (it == m_object.end())
+			return defaultValue;
+
+		return it->second;
+	}
+
+	/**
+	 * Overloaded function with type check.
+	 *
+	 * @param name the name
+	 * @param type the requested type
+	 * @param defaultValue the value replacement
+	 * @return the value or defaultValue
+	 */
+	template <typename DefaultValue>
+	Value valueOr(const std::string &name, Type type, DefaultValue &&defaultValue) const
+	{
+		if (m_type != Type::Object)
+			return defaultValue;
+
+		auto it = m_object.find(name);
+
+		if (it == m_object.end() || it->second.typeOf() != type)
+			return defaultValue;
+
+		return it->second;
+	}
+
+	/**
+	 * Get a value from the object.
+	 *
+	 * @pre must be an object
+	 * @param name the value key
+	 * @return the value
+	 * @throw std::out_of_range if not found
+	 */
+	inline const Value &at(const std::string &name) const
+	{
+		assert(isObject());
+
+		return m_object.at(name);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an object
+	 * @param name the value key
+	 * @return the value
+	 * @throw std::out_of_range if not found
+	 */
+	inline Value &at(const std::string &name)
+	{
+		assert(isObject());
+
+		return m_object.at(name);
+	}
+
+	/**
+	 * Get a value from the object.
+	 *
+	 * @pre must be an object
+	 * @param name the value key
+	 * @return the value
+	 */
+	inline Value &operator[](const std::string &name)
+	{
+		assert(isObject());
+
+		return m_object[name];
+	}
+
+	/**
+	 * Find a value by key.
+	 *
+	 * @pre must be an object
+	 * @param key the property key
+	 * @return the iterator or past the end if not found
+	 */
+	inline iterator find(const std::string &key)
+	{
+		assert(isObject());
+
+		return iterator(*this, m_object.find(key));
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an object
+	 * @param key the property key
+	 * @return the iterator or past the end if not found
+	 */
+	inline const_iterator find(const std::string &key) const
+	{
+		assert(isObject());
+
+		return const_iterator(*this, m_object.find(key));
+	}
+
+	/**
+	 * Insert a new value.
+	 *
+	 * @pre must be an object
+	 * @param name the key
+	 * @param value the value
+	 */
+	inline void insert(std::string name, const Value &value)
+	{
+		assert(isObject());
+
+		m_object.insert({std::move(name), value});
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre must be an object
+	 * @param name the key
+	 * @param value the value
+	 */
+	inline void insert(std::string name, Value &&value)
+	{
+		assert(isObject());
+
+		m_object.insert({std::move(name), std::move(value)});
+	}
+
+	/**
+	 * Check if a value exists.
+	 *
+	 * @pre must be an object
+	 * @param key the key value
+	 * @return true if exists
+	 */
+	inline bool contains(const std::string &key) const noexcept
+	{
+		assert(isObject());
+
+		return m_object.find(key) != m_object.end();
+	}
+
+	/**
+	 * Remove a value of the specified key.
+	 *
+	 * @pre must be an object
+	 * @param key the value key
+	 */
+	inline void erase(const std::string &key)
+	{
+		assert(isObject());
+
+		m_object.erase(key);
+	}
+
+	/**
+	 * Return this value as JSon representation.
+	 *
+	 * @param indent, the indentation to use (0 == compact, < 0 == tabs, > 0 == number of spaces)
+	 * @param tabs, use tabs or not
+	 * @return the string
+	 */
+	inline std::string toJson(int indent = 2) const
+	{
+		return toJson(indent, 0);
+	}
+};
+
+/**
+ * Escape the input.
+ *
+ * @param input the input
+ * @return the escaped string
+ */
+std::string escape(const std::string &input);
+
+/**
+ * Convenient function for creating array from initializer list.
+ *
+ * @param values the values
+ * @return the array
+ */
+inline Value array(std::initializer_list<Value> values)
+{
+	return Value(std::vector<Value>(values.begin(), values.end()));
+}
+
+/**
+ * Convenient function for creating object from initializer list.
+ *
+ * @param values the values
+ * @return the object
+ */
+inline Value object(std::initializer_list<std::pair<std::string, Value>> values)
+{
+	return Value(std::map<std::string, Value>(values.begin(), values.end()));
+}
+
+} // !json
+
+} // !irccd
+
+#endif // !_JSON_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/logger.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,259 @@
+/*
+ * logger.cpp -- irccd logging
+ *
+ * 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 <iostream>
+#include <stdexcept>
+#include <streambuf>
+
+#include <irccd-config.h>
+
+#if defined(HAVE_SYSLOG)
+#  include <syslog.h>
+#endif // !HAVE_SYSLOG
+
+#include "logger.h"
+#include "system.h"
+
+namespace irccd {
+
+namespace log {
+
+/* --------------------------------------------------------
+ * Buffer -- output buffer (private)
+ * -------------------------------------------------------- */
+
+/**
+ * @class LoggerBuffer
+ * @brief This class is a internal buffer for the Logger streams
+ */
+class Buffer : public std::stringbuf {
+private:
+	Interface *m_interface{nullptr};
+	Level m_level;
+
+public:
+	/**
+	 * Create the buffer with the specified level.
+	 *
+	 * @param level the level
+	 */
+	inline Buffer(Level level) noexcept
+		: m_level(level)
+	{
+	}
+
+	/**
+	 * Update the underlying interface.
+	 *
+	 * @param iface is a non-owning pointer to the new interface
+	 */
+	inline void setInterface(Interface *iface) noexcept
+	{
+		m_interface = iface;
+	}
+
+	/**
+	 * Sync the buffer by calling the interface if set.
+	 *
+	 * This function split the buffer line per line and remove it before
+	 * calling the appropriate interface function.
+	 */
+	virtual int sync() override
+	{
+#if defined(NDEBUG)
+		/*
+		 * Debug is disabled, don't call interface->write() but don't
+		 * forget to flush the buffer.
+		 */
+		if (m_level == Level::Debug) {
+			str("");
+
+			return 0;
+		}
+#endif
+
+		/* Verbose is disabled? Don't show and flush the buffer too. */
+		if (m_level == Level::Info && !isVerbose()) {
+			str("");
+
+			return 0;
+		}
+
+		std::string buffer = str();
+		std::string::size_type pos;
+
+		while ((pos = buffer.find("\n")) != std::string::npos) {
+			std::string line = buffer.substr(0, pos);
+
+			/* Remove this line */
+			buffer.erase(buffer.begin(), buffer.begin() + pos + 1);
+
+			if (m_interface)
+				m_interface->write(m_level, line);
+		}
+
+		str(buffer);
+
+		return 0;
+	}
+};
+
+/* --------------------------------------------------------
+ * Local variables
+ * -------------------------------------------------------- */
+
+namespace {
+
+/* Generic interface for all outputs */
+std::unique_ptr<Interface> iface;
+
+/* Internal buffers */
+Buffer bufferInfo(Level::Info);
+Buffer bufferWarning(Level::Warning);
+Buffer bufferDebug(Level::Debug);
+
+/* Stream outputs */
+std::ostream streamInfo(&bufferInfo);
+std::ostream streamWarning(&bufferWarning);
+std::ostream streamDebug(&bufferDebug);
+
+/* Options */
+bool verbose(false);
+
+} // !namespace
+
+/* --------------------------------------------------------
+ * Console
+ * -------------------------------------------------------- */
+
+void Console::write(Level level, const std::string &line) noexcept
+{
+	if (level == Level::Warning)
+		std::cerr << line << std::endl;
+	else
+		std::cout << line << std::endl;
+}
+
+/* --------------------------------------------------------
+ * File
+ * -------------------------------------------------------- */
+
+File::File(std::string normal, std::string errors)
+	: m_outputNormal(std::move(normal))
+	, m_outputError(std::move(errors))
+{
+}
+
+void File::write(Level level, const std::string &line) noexcept
+{
+	std::string &path = (level == Level::Warning) ? m_outputError : m_outputNormal;
+	std::ofstream output(path, std::ofstream::out | std::ofstream::app);
+
+	output << line << std::endl;
+}
+
+/* --------------------------------------------------------
+ * Silent
+ * -------------------------------------------------------- */
+
+void Silent::write(Level, const std::string &) noexcept
+{
+}
+
+/* --------------------------------------------------------
+ * Syslog
+ * -------------------------------------------------------- */
+
+#if defined(HAVE_SYSLOG)
+
+Syslog::Syslog()
+{
+	openlog(sys::programName().c_str(), LOG_PID, LOG_DAEMON);
+}
+
+Syslog::~Syslog()
+{
+	closelog();
+}
+
+void Syslog::write(Level level, const std::string &line) noexcept
+{
+	int syslogLevel;
+
+	switch (level) {
+	case Level::Warning:
+		syslogLevel = LOG_WARNING;
+		break;
+	case Level::Debug:
+		syslogLevel = LOG_DEBUG;
+		break;
+	case Level::Info:
+		/* FALLTHROUGH */
+	default:
+		syslogLevel = LOG_INFO;
+		break;
+	}
+
+	syslog(syslogLevel | LOG_USER, "%s", line.c_str());
+}
+
+#endif // !HAVE_SYSLOG
+
+/* --------------------------------------------------------
+ * Functions
+ * -------------------------------------------------------- */
+
+void setInterface(std::unique_ptr<Interface> ifaceValue) noexcept
+{
+	iface = std::move(ifaceValue);
+	bufferInfo.setInterface(iface.get());
+	bufferWarning.setInterface(iface.get());
+	bufferDebug.setInterface(iface.get());
+}
+
+std::ostream &info() noexcept
+{
+	return streamInfo;
+}
+
+std::ostream &warning() noexcept
+{
+	return streamWarning;
+}
+
+std::ostream &debug() noexcept
+{
+	return streamDebug;
+}
+
+bool isVerbose() noexcept
+{
+	return verbose;
+}
+
+void setVerbose(bool mode) noexcept
+{
+	verbose = mode;
+}
+
+} // !log
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/logger.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,217 @@
+/*
+ * logger.h -- irccd logging
+ *
+ * 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_LOGGER_H_
+#define _IRCCD_LOGGER_H_
+
+#include <irccd-config.h>
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+namespace irccd {
+
+namespace log {
+
+/**
+ * @enum Level
+ * @brief Which level of warning
+ */
+enum class Level {
+	Info,			//!< Standard information (disabled if verbose is false)
+	Warning,		//!< Warning (always shown)
+	Debug			//!< Debug message (only if compiled in debug mode)
+};
+
+/* --------------------------------------------------------
+ * Interface -- abstract logging interface
+ * -------------------------------------------------------- */
+
+/**
+ * @class Interface
+ * @brief Interface to implement new logger mechanisms
+ *
+ * Derive from this class and use Logger::setInterface() to change logging
+ * system.
+ *
+ * @see File
+ * @see Console
+ * @see Syslog
+ * @see Silent
+ */
+class Interface {
+public:
+	/**
+	 * Write the line to the logs. The line to write will never contains
+	 * trailing new line character.
+	 *
+	 * @param level the level
+	 * @param line the line without trailing \n
+	 */	
+	virtual void write(Level level, const std::string &line) noexcept = 0;
+};
+
+/* --------------------------------------------------------
+ * Console -- logs to console
+ * -------------------------------------------------------- */
+
+/**
+ * @class Console
+ * @brief Logger implementation for console output
+ */
+class Console : public Interface {
+public:
+	/**
+	 * @copydoc Interface::write
+	 */
+	void write(Level level, const std::string &line) noexcept override;
+};
+
+/* --------------------------------------------------------
+ * File -- logs to a file
+ * -------------------------------------------------------- */
+
+/**
+ * @class File
+ * @brief Output to a file
+ */
+class File : public Interface {
+private:
+	std::string m_outputNormal;
+	std::string m_outputError;
+
+public:
+	/**
+	 * Outputs to files. Info and Debug are written in normal and Warnings
+	 * in errors.
+	 *
+	 * The same path can be used for all levels.
+	 *
+	 * @param normal the path to the normal logs
+	 * @param errors the path to the errors logs
+	 */
+	File(std::string normal, std::string errors);
+
+	/**
+	 * @copydoc Interface::write
+	 */
+	void write(Level level, const std::string &line) noexcept override;
+};
+
+/* --------------------------------------------------------
+ * Silent -- disable all logs
+ * -------------------------------------------------------- */
+
+/**
+ * @class Silent
+ * @brief Use to disable logs
+ *
+ * Useful for unit tests when some classes may emits log.
+ */
+class Silent : public Interface {
+public:
+	/**
+	 * @copydoc Interface::write
+	 */
+	void write(Level level, const std::string &line) noexcept override;
+};
+
+/* --------------------------------------------------------
+ * Syslog -- system logger
+ * -------------------------------------------------------- */
+
+#if defined(HAVE_SYSLOG)
+
+/**
+ * @class Syslog
+ * @brief Implements logger into syslog
+ */
+class Syslog : public Interface {
+public:
+	/**
+	 * Open the syslog.
+	 */
+	Syslog();
+
+	/**
+	 * Close the syslog.
+	 */
+	~Syslog();
+
+	/**
+	 * @copydoc Interface::write
+	 */
+	void write(Level level, const std::string &line) noexcept override;
+};
+
+#endif // !HAVE_SYSLOG
+
+/* --------------------------------------------------------
+ * Functions
+ * -------------------------------------------------------- */
+
+/**
+ * Update the logger interface.
+ *
+ * @param iface the new interface
+ */
+void setInterface(std::unique_ptr<Interface> iface) noexcept;
+
+/**
+ * Get the stream for informational messages.
+ *
+ * @return the stream
+ * @note Has no effect if verbose is set to false.
+ */
+std::ostream &info() noexcept;
+
+/**
+ * Get the stream for warnings.
+ *
+ * @return the stream
+ */
+std::ostream &warning() noexcept;
+
+/**
+ * Get the stream for debug messages.
+ *
+ * @return the stream
+ * @note Has no effect if compiled in release mode.
+ */
+std::ostream &debug() noexcept;
+
+/**
+ * Tells if verbose is enabled.
+ *
+ * @return true if enabled
+ */
+bool isVerbose() noexcept;
+
+/**
+ * Set the verbosity mode.
+ *
+ * @param mode the new mode
+ */
+void setVerbose(bool mode) noexcept;
+
+} // !log
+
+} // !irccd
+
+#endif // !_IRCCD_LOGGER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/options.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,184 @@
+/*
+ * options.cpp -- parse Unix command line options
+ *
+ * Copyright (c) 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 <cassert>
+
+#include "options.h"
+
+namespace irccd {
+
+namespace parser {
+
+namespace {
+
+using Iterator = std::vector<std::string>::iterator;
+using Args = std::vector<std::string>;
+
+inline bool isOption(const std::string &arg) noexcept
+{
+	return arg.size() >= 2 && arg[0] == '-';
+}
+
+inline bool isLongOption(const std::string &arg) noexcept
+{
+	assert(isOption(arg));
+
+	return arg.size() >= 3 && arg[1] == '-';
+}
+
+inline bool isShortSimple(const std::string &arg) noexcept
+{
+	assert(isOption(arg));
+	assert(!isLongOption(arg));
+
+	return arg.size() == 2;
+}
+
+void parseLongOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
+{
+	auto arg = *it++;
+	auto opt = definition.find(arg);
+
+	if (opt == definition.end())
+		throw InvalidOption(arg);
+
+	/* Need argument? */
+	if (opt->second) {
+		if (it == end || isOption(*it))
+			throw MissingValue(arg);
+
+		result.insert(std::make_pair(arg, *it++));
+		it = args.erase(args.begin(), it);
+		end = args.end();
+	} else {
+		result.insert(std::make_pair(arg, ""));
+		it = args.erase(args.begin());
+		end = args.end();
+	}
+}
+
+void parseShortOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
+{
+	if (isShortSimple(*it)) {
+		/*
+		 * Here two cases:
+		 *
+		 * -v (no option)
+		 * -c value
+		 */
+		auto arg = *it++;
+		auto opt = definition.find(arg);
+
+		if (opt == definition.end())
+			throw InvalidOption(arg);
+
+		/* Need argument? */
+		if (opt->second) {
+			if (it == end || isOption(*it))
+				throw MissingValue(arg);
+
+			result.insert(std::make_pair(arg, *it++));
+			it = args.erase(args.begin(), it);
+			end = args.end();
+		} else {
+			result.insert(std::make_pair(arg, ""));
+			it = args.erase(args.begin());
+			end = args.end();
+		}
+	} else {
+		/*
+		 * Here multiple scenarios:
+		 *
+		 * 1. -abc (-a -b -c if all are simple boolean arguments)
+		 * 2. -vc foo.conf (-v -c foo.conf if -c is argument dependant)
+		 * 3. -vcfoo.conf (-v -c foo.conf also)
+		 */
+		auto value = it->substr(1);
+		auto len = value.length();
+		int toremove = 1;
+
+		for (decltype(len) i = 0; i < len; ++i) {
+			auto arg = std::string{'-'} + value[i];
+			auto opt = definition.find(arg);
+
+			if (opt == definition.end())
+				throw InvalidOption(arg);
+
+			if (opt->second) {
+				if (i == (len - 1)) {
+					/* End of string, get the next argument (see 2.) */
+					if (++it == end || isOption(*it))
+						throw MissingValue(arg);
+
+					result.insert(std::make_pair(arg, *it));
+					toremove += 1;
+				} else {
+					result.insert(std::make_pair(arg, value.substr(i + 1)));
+					i = len;
+				}
+			} else {
+				result.insert(std::make_pair(arg, ""));
+			}
+		}
+
+		it = args.erase(args.begin(), args.begin() + toremove);
+		end = args.end();
+	}
+}
+
+} // !namespace
+
+Result read(std::vector<std::string> &args, const Options &definition)
+{
+	Result result;
+
+	auto it = args.begin();
+	auto end = args.end();
+
+	while (it != end) {
+		if (!isOption(*it))
+			break;
+
+		if (isLongOption(*it))
+			parseLongOption(result, args, it, end, definition);
+		else
+			parseShortOption(result, args, it, end, definition);
+	}
+
+	return result;
+}
+
+Result read(int &argc, char **&argv, const Options &definition)
+{
+	std::vector<std::string> args;
+
+	for (int i = 0; i < argc; ++i)
+		args.push_back(argv[i]);
+
+	auto before = args.size();
+	auto result = read(args, definition);
+
+	argc -= before - args.size();
+	argv += before - args.size();
+
+	return result;
+}
+
+} // !parser
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/options.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,145 @@
+/*
+ * options.h -- parse Unix command line options
+ *
+ * Copyright (c) 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 _OPTION_PARSER_H_
+#define _OPTION_PARSER_H_
+
+#include <exception>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * Namespace for options parsing.
+ */
+namespace parser {
+
+/**
+ * @class InvalidOption
+ * @brief This exception is thrown when an invalid option has been found.
+ */
+class InvalidOption : public std::exception {
+private:
+	std::string message;
+
+public:
+	/**
+	 * The invalid option given.
+	 */
+	std::string argument;
+
+	/**
+	 * Construct the exception.
+	 *
+	 * @param arg the argument missing
+	 */
+	inline InvalidOption(std::string arg)
+		: argument(std::move(arg))
+	{
+		message = std::string("invalid option: ") + argument;
+	}
+
+	/**
+	 * Get the error message.
+	 *
+	 * @return the error message
+	 */
+	const char *what() const noexcept override
+	{
+		return message.c_str();
+	}
+};
+
+/**
+ * @class MissingValue
+ * @brief This exception is thrown when an option requires a value and no value has been given
+ */
+class MissingValue : public std::exception {
+private:
+	std::string message;
+
+public:
+	/**
+	 * The argument that requires a value.
+	 */
+	std::string argument;
+
+	/**
+	 * Construct the exception.
+	 *
+	 * @param arg the argument that requires a value
+	 */
+	inline MissingValue(std::string arg)
+		: argument(std::move(arg))
+	{
+		message = std::string("missing argument for: ") + argument;
+	}
+
+	/**
+	 * Get the error message.
+	 *
+	 * @return the error message
+	 */
+	const char *what() const noexcept override
+	{
+		return message.c_str();
+	}
+};
+
+/**
+ * Packed multimap of options.
+ */
+using Result = std::multimap<std::string, std::string>;
+
+/**
+ * Define the allowed options.
+ */
+using Options = std::map<std::string, bool>;
+
+/**
+ * Extract the command line options and return a result.
+ *
+ * @param args the arguments
+ * @param definition
+ * @warning the arguments vector is modified in place to remove parsed options
+ * @throw MissingValue
+ * @throw InvalidOption
+ */
+Result read(std::vector<std::string> &args, const Options &definition);
+
+/**
+ * Overloaded function for usage with main() arguments.
+ *
+ * @param argc the number of arguments
+ * @param argv the argument vector
+ * @param definition
+ * @note don't forget to remove the first argv[0] argument
+ * @warning the argc and argv are modified in place to remove parsed options
+ * @throw MissingValue
+ * @throw InvalidOption
+ */
+Result read(int &argc, char **&argv, const Options &definition);
+
+} // !parser
+
+} // !irccd
+
+#endif // !_OPTION_PARSER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/path.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,554 @@
+/*
+ * path.cpp -- special paths inside 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 <algorithm>
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+
+#include <irccd-config.h>
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+#  include <Windows.h>
+#  include <Shlobj.h>
+#else
+#  if defined(IRCCD_SYSTEM_LINUX)
+#    include <limits.h>
+#    include <unistd.h>
+#    include <cerrno>
+#    include <cstring>
+#    include <stdexcept>
+#  endif
+
+#  if defined(IRCCD_SYSTEM_FREEBSD)
+#    include <sys/types.h>
+#    include <sys/sysctl.h>
+#    include <limits.h>
+
+#    include <array>
+#    include <cerrno>
+#    include <cstring>
+#    include <stdexcept>
+#  endif
+
+#  if defined(IRCCD_SYSTEM_MAC)
+#    include <cerrno>
+#    include <cstring>
+#    include <unistd.h>
+#    include <libproc.h>
+#  endif
+
+#  include <xdg.h>
+#endif
+
+#include "private/filesystem.h"
+#include "path.h"
+#include "system.h"
+#include "util.h"
+
+namespace irccd {
+
+namespace path {
+
+namespace {
+
+/*
+ * Base program directory
+ * ------------------------------------------------------------------
+ *
+ * This variable stores the program base directory. It is only enabled when irccd is relocatable because we can
+ * retrieve the base directory by removing WITH_BINDIR.
+ *
+ * If it is empty, the program was not able to detect it (e.g. error, not supported).
+ */
+
+#if defined(IRCCD_RELOCATABLE)
+
+std::string base;
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+
+std::string executablePath()
+{
+	std::string result;
+	std::size_t size = PATH_MAX;
+	
+	result.resize(size);
+	
+	if (!(size = GetModuleFileNameA(nullptr, &result[0], size)))
+		throw std::runtime_error("GetModuleFileName error");
+	
+	result.resize(size);
+	
+	return result;
+}
+
+#elif defined(IRCCD_SYSTEM_LINUX)
+
+std::string executablePath()
+{
+	std::string result;
+	
+	result.resize(2048);
+	
+	auto size = readlink("/proc/self/exe", &result[0], 2048);
+	
+	if (size < 0)
+		throw std::invalid_argument(std::strerror(errno));
+	
+	result.resize(size);
+	
+	return result;
+}
+
+#elif defined(IRCCD_SYSTEM_FREEBSD)
+
+std::string executablePath()
+{
+	std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } };
+	std::string result;
+	std::size_t size = PATH_MAX + 1;
+	
+	result.resize(size);
+	
+	if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0)
+		throw std::runtime_error(std::strerror(errno));
+	
+	result.resize(size);
+	
+	return result;
+}
+
+#elif defined(IRCCD_SYSTEM_MAC)
+
+std::string executablePath()
+{
+	std::string result;
+	std::size_t size = PROC_PIDPATHINFO_MAXSIZE;
+	
+	result.resize(size);
+	
+	if ((size = proc_pidpath(getpid(), &result[0], size)) == 0)
+		throw std::runtime_error(std::strerror(errno));
+	
+	result.resize(size);
+	
+	return result;
+}
+
+#else
+
+/*
+ * TODO: add support for more systems here.
+ *
+ *  - NetBSD
+ *  - OpenBSD
+ */
+
+std::string executablePath()
+{
+	return "";
+}
+
+#endif
+
+#endif // !IRCCD_RELOCATABLE
+
+/*
+ * System paths
+ * ------------------------------------------------------------------
+ *
+ * Compute system paths.
+ *
+ * Do not call any of these functions if irccd is relocatable and base is unset.
+ */
+
+std::string systemConfig()
+{
+#if defined(IRCCD_RELOCATABLE)
+	assert(!base.empty());
+
+	return base + WITH_CONFDIR;
+#else
+	return fs::isAbsolute(WITH_CONFDIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_CONFDIR;
+#endif
+}
+
+std::string systemData()
+{
+#if defined(IRCCD_RELOCATABLE)
+	assert(!base.empty());
+
+	return base + WITH_DATADIR;
+#else
+	return fs::isAbsolute(WITH_DATADIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_DATADIR;
+#endif
+}
+
+std::string systemCache()
+{
+#if defined(IRCCD_RELOCATABLE)
+	assert(!base.empty());
+
+	return base + WITH_CACHEDIR;
+#else
+	return fs::isAbsolute(WITH_CACHEDIR) ? WITH_CACHEDIR : std::string(PREFIX) + fs::Separator + WITH_CACHEDIR;
+#endif
+}
+
+std::string systemPlugins()
+{
+#if defined(IRCCD_RELOCATABLE)
+	assert(!base.empty());
+
+	return base + WITH_PLUGINDIR;
+#else
+	return fs::isAbsolute(WITH_PLUGINDIR) ? WITH_PLUGINDIR : std::string(PREFIX) + fs::Separator + WITH_PLUGINDIR;
+#endif
+}
+
+/*
+ * User paths
+ * ------------------------------------------------------------------
+ *
+ * Compute user paths.
+ */
+
+/*
+ * userConfig
+ * ---------------------------------------------------------
+ *
+ * Get the path directory to the user configuration. Example:
+ *
+ * Unix:
+ *
+ * XDG_CONFIG_HOME/irccd
+ * HOME/.config/irccd
+ *
+ * Windows:
+ *
+ * CSIDL_LOCAL_APPDATA/irccd/config
+ */
+std::string userConfig()
+{
+	std::ostringstream oss;
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	char path[MAX_PATH];
+
+	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK) {
+		oss << "";
+	} else {
+		oss << path;
+		oss << "\\irccd\\config\\";
+	}
+#else
+	try {
+		Xdg xdg;
+
+		oss << xdg.configHome();
+		oss << "/irccd/";
+	} catch (const std::exception &) {
+		const char *home = getenv("HOME");
+
+		if (home != nullptr)
+			oss << home;
+
+		oss << "/.config/irccd/";
+	}
+#endif
+
+	return oss.str();
+}
+
+/*
+ * userData
+ * --------------------------------------------------------
+ *
+ * Get the path to the data application.
+ *
+ * Unix:
+ *
+ * XDG_DATA_HOME/irccd
+ * HOME/.local/share/irccd
+ *
+ * Windows:
+ *
+ * CSIDL_LOCAL_APPDATA
+ */
+std::string userData()
+{
+	std::ostringstream oss;
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	char path[MAX_PATH];
+
+	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK)
+		oss << "";
+	else {
+		oss << path;
+		oss << "\\irccd\\share";
+	}
+#else
+	try {
+		Xdg xdg;
+
+		oss << xdg.dataHome();
+		oss << "/irccd/";
+	} catch (const std::exception &) {
+		const char *home = getenv("HOME");
+
+		if (home != nullptr)
+			oss << home;
+
+		oss << "/.local/share/irccd/";
+	}
+#endif
+
+	return oss.str();
+}
+
+/*
+ * userCache
+ * --------------------------------------------------------
+ *
+ * Directory for cache files.
+ *
+ * Unix:
+ *
+ * XDG_CACHE_HOME/irccd
+ * HOME/.cache/irccd
+ *
+ * Windows:
+ *
+ * %TEMP% (e.g. C:\Users\<user>\AppData\Local\Temp)
+ */
+std::string userCache()
+{
+	std::ostringstream oss;
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	char path[MAX_PATH + 1];
+
+	GetTempPathA(sizeof (path), path);
+
+	oss << path << "\\irccd\\";
+#else
+	try {
+		Xdg xdg;
+
+		oss << xdg.cacheHome();
+		oss << "/irccd/";
+	} catch (const std::exception &) {
+		const char *home = getenv("HOME");
+
+		if (home != nullptr)
+			oss << home;
+
+		oss << "/.cache/irccd/";
+	}
+#endif
+
+	return oss.str();
+}
+
+/*
+ * userPlugins
+ * --------------------------------------------------------
+ *
+ * Path to the data + plugins.
+ */
+std::string userPlugins()
+{
+	return userData() + "/plugins/";
+}
+
+} // !namespace
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+const char Separator(';');
+#else
+const char Separator(':');
+#endif
+
+void setApplicationPath(const std::string &argv0)
+{
+#if defined(IRCCD_RELOCATABLE)
+	try {
+		base = executablePath();
+	} catch (const std::exception &) {
+		/*
+		 * If an exception is thrown, that means the operatin system supports a function to get the executable
+		 * path but it failed.
+		 *
+		 * TODO: show a waning
+		 */
+	}
+
+	/*
+	 * If we could not get the application path from the native function, check if argv[0] is an absolute path
+	 * and use that from there.
+	 *
+	 * Otherwise, search from the PATH.
+	 *
+	 * In the worst case use current working directory.
+	 */
+	if (base.empty()) {
+		if (fs::isAbsolute(argv0)) {
+			base = argv0;
+		} else {
+			std::string name = fs::baseName(argv0);
+
+			for (const auto &dir : util::split(sys::env("PATH"), std::string(1, Separator))) {
+				std::string path = dir + fs::Separator + name;
+
+				if (fs::exists(path)) {
+					base = path;
+					break;
+				}
+			}
+
+			/* Not found in PATH? add dummy value */
+			if (base.empty())
+				base = std::string(".") + fs::Separator + WITH_BINDIR + fs::Separator + "dummy";
+		}
+	}
+
+	/* Find bin/<progname> */
+	auto pos = base.rfind(std::string(WITH_BINDIR) + fs::Separator + fs::baseName(base));
+
+	if (pos != std::string::npos)
+		base.erase(pos);
+
+	/* Add trailing / or \\ for convenience */
+	base = clean(base);
+
+	assert(!base.empty());
+#else
+	(void)argv0;
+#endif
+}
+
+std::string clean(std::string input)
+{
+	if (input.empty())
+		return input;
+
+	/* First, remove any duplicates */
+	input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) {
+		return c1 == c2 && (c1 == '/' || c1 == '\\');
+	}), input.end());
+
+	/* Add a trailing / or \\ */
+	char c = input[input.length() - 1];
+	if (c != '/' && c != '\\')
+		input += fs::Separator;
+
+	/* Now converts all / to \\ for Windows and the opposite for Unix */
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	std::replace(input.begin(), input.end(), '/', '\\');
+#else
+	std::replace(input.begin(), input.end(), '\\', '/');
+#endif
+
+	return input;
+}
+
+std::string get(Path path, Owner owner)
+{
+	assert(path >= PathConfig && path <= PathPlugins);
+	assert(owner >= OwnerSystem && owner <= OwnerUser);
+
+	std::string result;
+
+	switch (owner) {
+	case OwnerSystem:
+		switch (path) {
+		case PathCache:
+			result = clean(systemCache());
+			break;
+		case PathConfig:
+			result = clean(systemConfig());
+			break;
+		case PathData:
+			result = clean(systemData());
+			break;
+		case PathPlugins:
+			result = clean(systemPlugins());
+			break;
+		default:
+			break;
+		}
+	case OwnerUser:
+		switch (path) {
+		case PathCache:
+			result = clean(userCache());
+			break;
+		case PathConfig:
+			result = clean(userConfig());
+			break;
+		case PathData:
+			result = clean(userData());
+			break;
+		case PathPlugins:
+			result = clean(userPlugins());
+			break;
+		default:
+			break;
+		}
+	default:
+		break;
+	}
+
+	return result;
+}
+
+std::vector<std::string> list(Path path)
+{
+	assert(path >= PathConfig && path <= PathPlugins);
+
+	std::vector<std::string> list;
+
+	switch (path) {
+	case PathCache:
+		list.push_back(clean(userCache()));
+		list.push_back(clean(systemCache()));
+		break;
+	case PathConfig:
+		list.push_back(clean(userConfig()));
+		list.push_back(clean(systemConfig()));
+		break;
+	case PathData:
+		list.push_back(clean(userData()));
+		list.push_back(clean(systemData()));
+		break;
+	case PathPlugins:
+		list.push_back(clean(fs::cwd()));
+		list.push_back(clean(userPlugins()));
+		list.push_back(clean(systemPlugins()));
+		break;
+	default:
+		break;
+	}
+
+	return list;
+}
+
+} // !path
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/path.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,100 @@
+/*
+ * path.h -- special paths inside 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_PATH_H_
+#define _IRCCD_PATH_H_
+
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+namespace path {
+
+/**
+ * PATH separator, either : or ;.
+ */
+extern const char Separator;
+
+/**
+ * @enum Path
+ * @brief Which special path to get
+ */
+enum Path {
+	PathConfig,			//!< Configuration files
+	PathData,			//!< Data directory
+	PathCache,			//!< Cache files
+	PathPlugins			//!< Path to the plugins
+};
+
+/**
+ * @enum Owner
+ * @brief For paths, get the installation path or the user ones
+ */
+enum Owner {
+	OwnerSystem,			//!< System wide
+	OwnerUser			//!< User
+};
+
+/**
+ * This function must be called before at the beginning of the main.
+ *
+ * It use system dependant program path lookup if available and fallbacks to the path given as argument if any failure
+ * was encoutered.
+ *
+ * @param argv0 the path to the executable (argv[0])
+ */
+void setApplicationPath(const std::string &argv0);
+
+/**
+ * Clean a path by removing any extra / or \ and add a trailing one.
+ *
+ * @param path the path
+ * @return the updated path
+ */
+std::string clean(std::string path);
+
+/**
+ * Generic function for path retrievement.
+ *
+ * The path is always terminated by a trailing / or \\.
+ *
+ * @pre setApplicationPath must have been called
+ * @param path the type of path
+ * @param owner system or user wide
+ * @return the path
+ */
+std::string get(Path path, Owner owner);
+
+/**
+ * Generic function for multiple paths.
+ *
+ * This function will add more directories than pathSystem*() and pathUser*() functions, for example
+ * it will add some path if irccd is relocatable.
+ *
+ * @pre setApplicationPath must have been called
+ * @param path the type of path
+ * @return the list of preferred directories in order
+ */
+std::vector<std::string> list(Path path);
+
+} // !path
+
+} // !irccd
+
+#endif // !_IRCCD_PATH_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/plugin.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,482 @@
+/*
+ * plugin.cpp -- irccd JavaScript plugin interface
+ *
+ * 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 <stdexcept>
+
+#include <irccd-config.h>
+
+#if defined(HAVE_STAT)
+#  include <sys/stat.h>
+#  include <cerrno>
+#  include <cstring>
+#endif
+
+#include "private/filesystem.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"
+
+using namespace std;
+
+namespace irccd {
+
+void Plugin::call(const string &name, unsigned nargs)
+{
+	m_context.getGlobal<void>(name);
+
+	if (m_context.type(-1) == DUK_TYPE_UNDEFINED) {
+		/* Function not defined, remove the undefined value and all arguments */
+		m_context.pop(nargs + 1);
+	} else {
+		/* Call the function and discard the result */
+		m_context.insert(-nargs - 1);
+		m_context.pcall(nargs);
+		m_context.pop();
+	}
+}
+
+void Plugin::putVars()
+{
+	js::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);
+}
+
+void Plugin::putPath(const std::string &varname, const std::string &append, path::Path type)
+{
+	js::StackAssert sa(m_context);
+
+	bool found = true;
+	std::string foundpath;
+
+	/*
+	 * Use the first existing directory available.
+	 */
+	for (const std::string &p : path::list(type)) {
+		foundpath = path::clean(p + append);
+
+		if (fs::exists(foundpath)) {
+			found = true;
+			break;
+		}
+	}
+
+	/* Use the system as default */
+	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);
+}
+
+void Plugin::putPaths()
+{
+	js::StackAssert sa(m_context);
+
+	/*
+	 * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/)
+	 * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/)
+	 */
+	putPath("dataPath", "plugin/" + m_info.name, path::PathData);
+	putPath("configPath", "plugin/" + m_info.name, path::PathConfig);
+	putPath("cachePath", "plugin/" + m_info.name, path::PathCache);
+}
+
+void Plugin::putConfig(const PluginConfig &config)
+{
+	js::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");
+
+	if (m_context.type(-1) != DUK_TYPE_OBJECT) {
+		m_context.pop();
+		m_context.push(js::Object{});
+	}
+
+	m_context.push(config);
+	m_context.putProperty(-2, "config");
+	m_context.pop(2);
+}
+
+Plugin::Plugin(std::string name, std::string path, const PluginConfig &config)
+{
+	js::StackAssert sa(m_context);
+
+	m_info.name = std::move(name);
+	m_info.path = std::move(path);
+
+	/*
+	 * Duktape currently emit useless warnings when a file do
+	 * not exists so we do a homemade access.
+	 */
+#if defined(HAVE_STAT)
+	struct stat st;
+
+	if (stat(m_info.path.c_str(), &st) < 0)
+		throw std::runtime_error(std::strerror(errno));
+#endif
+
+	/*
+	 * Store the base path to the plugin, it is required for
+	 * Duktape.modSearch to find external modules and other
+	 * sources.
+	 *
+	 * If path is absolute, the parent is the directory name, otherwise
+	 * we use the current working directory (needed for some tests).
+	 */
+	if (fs::isAbsolute(m_info.path))
+		m_info.parent = fs::dirName(m_info.path);
+	else
+		m_info.parent = fs::cwd();
+
+	/* Load standard irccd API */
+	loadJsIrccd(m_context);
+	loadJsDirectory(m_context);
+	loadJsElapsedTimer(m_context);
+	loadJsFile(m_context);
+	loadJsLogger(m_context);
+	loadJsPlugin(m_context);
+	loadJsServer(m_context);
+	loadJsSystem(m_context);
+	loadJsTimer(m_context);
+	loadJsUnicode(m_context);
+	loadJsUtil(m_context);
+
+	putVars();
+	putPaths();
+
+	/* Try to load the file (does not call onLoad yet) */
+	m_context.peval(js::File{m_info.path});
+	m_context.pop();
+
+	/* Initialize user defined options after loading to allow the plugin to define default values */
+	putConfig(config);
+
+	/* Read metadata */
+	m_context.getGlobal<void>("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");
+	}
+
+	m_context.pop();
+
+	log::debug() << "plugin " << m_info.name << ": " << std::endl;
+	log::debug() << "  author:  " << m_info.author << std::endl;
+	log::debug() << "  license: " << m_info.license << std::endl;
+	log::debug() << "  summary: " << m_info.summary << std::endl;
+	log::debug() << "  version: " << m_info.version << std::endl;
+}
+
+const PluginInfo &Plugin::info() const
+{
+	return m_info;
+}
+
+void Plugin::addTimer(std::shared_ptr<Timer> timer) noexcept
+{
+	std::weak_ptr<Timer> ptr(timer);
+
+	/*
+	 * These signals are called from the Timer thread and are transmitted to irccd so that it can
+	 * calls appropriate timer functions.
+	 */
+	timer->onSignal.connect([this, ptr] () {
+		auto timer = ptr.lock();
+
+		if (timer)
+			onTimerSignal(move(timer));
+	});
+	timer->onEnd.connect([this, ptr] () {
+		auto timer = ptr.lock();
+
+		if (timer)
+			onTimerEnd(move(timer));
+	});
+
+	m_timers.insert(move(timer));
+}
+
+void Plugin::removeTimer(const std::shared_ptr<Timer> &timer) noexcept
+{
+	/* 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())));
+
+	/* Remove from list */
+	m_timers.erase(timer);
+}
+
+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);
+
+	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));
+	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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(move(message));
+	call("onCommand", 4);
+}
+
+void Plugin::onConnect(std::shared_ptr<Server> server)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	call("onInvite", 3);
+}
+
+void Plugin::onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(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);
+
+	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));
+	call("onKick", 5);
+}
+
+void Plugin::onLoad()
+{
+	js::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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(move(message));
+	call("onMe", 4);
+}
+
+void Plugin::onMode(std::shared_ptr<Server> server, std::string origin, std::string mode)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(channel));
+	m_context.push(move(names));
+	call("onNames", 3);
+}
+
+void Plugin::onNick(std::shared_ptr<Server> server, std::string oldnick, std::string newnick)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(oldnick));
+	m_context.push(move(newnick));
+	call("onNick", 3);
+}
+
+void Plugin::onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(move(reason));
+	call("onPart", 4);
+}
+
+void Plugin::onQuery(std::shared_ptr<Server> server, std::string origin, std::string message)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(message));
+	call("onQuery", 3);
+}
+
+void Plugin::onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message)
+{
+	js::StackAssert sa(m_context);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(message));
+	call("onQueryCommand", 3);
+}
+
+void Plugin::onReload()
+{
+	js::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);
+
+	m_context.push(js::Shared<Server>{server});
+	m_context.push(move(origin));
+	m_context.push(move(channel));
+	m_context.push(move(topic));
+	call("onTopic", 4);
+}
+
+void Plugin::onUnload()
+{
+	js::StackAssert sa(m_context);
+
+	call("onUnload");
+}
+
+void Plugin::onWhois(std::shared_ptr<Server> server, ServerWhois whois)
+{
+	js::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);
+	call("onWhois", 2);
+}
+
+namespace js {
+
+void TypeInfo<PluginInfo>::push(Context &ctx, const PluginInfo &info)
+{
+	js::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);
+}
+
+} // !js
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/plugin.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,357 @@
+/*
+ * plugin.h -- irccd JavaScript plugin interface
+ *
+ * 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_H_
+#define _IRCCD_PLUGIN_H_
+
+/**
+ * @file Plugin.h
+ * @brief Irccd plugins
+ */
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <irccd/path.h>
+#include <irccd/private/signals.h>
+
+#include "js/js.h"
+
+#include "timer.h"
+
+namespace irccd {
+
+class Server;
+class ServerWhois;
+
+/**
+ * @class PluginInfo
+ * @brief Plugin information
+ */
+class PluginInfo {
+public:
+	std::string name;		//!< plugin name (from file on disk)
+	std::string parent;		//!< parent directory
+	std::string path;		//!< full path to the plugin file
+
+	/* Metadata */
+	std::string author{"unknown"};	//!< plugin author
+	std::string license{"unknown"};	//!< plugin license
+	std::string summary{"unknown"};	//!< short plugin description
+	std::string version{"unknown"};	//!< plugin version
+};
+
+/**
+ * Configuration map extract from config file.
+ */
+using PluginConfig = std::unordered_map<std::string, std::string>;
+
+/**
+ * Timers that a plugin owns.
+ */
+using PluginTimers = std::unordered_set<std::shared_ptr<Timer>>;
+
+/**
+ * @class Plugin
+ * @brief JavaScript plugin
+ *
+ * A plugin is identified by name and can be loaded and unloaded
+ * at runtime.
+ */
+class Plugin {
+public:
+	/**
+	 * Signal: onTimerSignal
+	 * ------------------------------------------------
+	 *
+	 * When a timer expires.
+	 *
+	 * Arguments:
+	 * - the timer object
+	 */
+	Signal<std::shared_ptr<Timer>> onTimerSignal;
+
+	/**
+	 * Signal: onTimerEnd
+	 * ------------------------------------------------
+	 *
+	 * When a timer is finished.
+	 *
+	 * Arguments:
+	 * - the timer object
+	 */
+	Signal<std::shared_ptr<Timer>> onTimerEnd;
+
+private:
+	/* JavaScript context */
+	js::Context m_context;
+
+	/* Plugin info and its timers */
+	PluginInfo m_info;
+	PluginTimers m_timers;
+
+	/* Private helpers */
+	void call(const std::string &name, unsigned nargs = 0);
+	void putVars();
+	void putPath(const std::string &varname, const std::string &append, path::Path type);
+	void putPaths();
+	void putConfig(const PluginConfig &config);
+
+public:
+	/**
+	 * Correct constructor.
+	 *
+	 * @param name the plugin id
+	 * @param path the fully resolved path to the plugin
+	 * @param config the plugin configuration
+	 * @throws std::runtime_error on errors
+	 */
+	Plugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
+
+	/**
+	 * Get the plugin information.
+	 */
+	const PluginInfo &info() const;
+
+	/**
+	 * Add a timer to the plugin.
+	 *
+	 * @param timer the timer to add
+	 */
+	void addTimer(std::shared_ptr<Timer> timer) noexcept;
+
+	/**
+	 * Remove a timer from a plugin.
+	 *
+	 * @param timer
+	 */
+	void removeTimer(const std::shared_ptr<Timer> &timer) noexcept;
+
+	/**
+	 * Access the Duktape context.
+	 *
+	 * @return the context
+	 */
+	inline js::Context &context() noexcept
+	{
+		return m_context;
+	}
+
+	/**
+	 * On channel message. This event will call onMessage or
+	 * onCommand if the messages starts with the command character
+	 * plus the plugin name.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the message
+	 * @param channel the channel
+	 * @param message the message or command
+	 */
+	void onCommand(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
+
+	/**
+	 * On successful connection.
+	 *
+	 * @param server the server
+	 */
+	void onConnect(std::shared_ptr<Server> server);
+
+	/**
+	 * On channel mode.
+	 *
+	 * @param server the server
+	 * @param origin the ouser who has changed the mode
+	 * @param channel the channel
+	 * @param mode the mode
+	 * @param arg the optional mode argument
+	 */
+	void onChannelMode(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg);
+
+	/**
+	 * On a channel notice.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the notice
+	 * @param channel on which channel
+	 * @param notice the message
+	 */
+	void onChannelNotice(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string notice);
+
+	/**
+	 * On invitation.
+	 *
+	 * @param server the server
+	 * @param origin the user who invited you
+	 * @param channel the channel
+	 */
+	void onInvite(std::shared_ptr<Server> server, std::string origin, std::string channel);
+
+	/**
+	 * On join.
+	 *
+	 * @param server the server
+	 * @param origin the user who joined
+	 * @param channel the channel
+	 */
+	void onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel);
+
+	/**
+	 * On kick.
+	 *
+	 * @param server the server
+	 * @param origin the user who kicked the target
+	 * @param channel the channel
+	 * @param target the kicked target
+	 * @param reason the optional reason
+	 */
+	void onKick(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason);
+
+	/**
+	 * On load.
+	 */
+	void onLoad();
+
+	/**
+	 * On channel message.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the message
+	 * @param channel the channel
+	 * @param message the message or command
+	 */
+	void onMessage(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
+
+	/**
+	 * On CTCP Action.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the message
+	 * @param channel the channel (may also be your nickname)
+	 * @param message the message
+	 */
+	void onMe(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message);
+
+	/**
+	 * On user mode change.
+	 *
+	 * @param server the server
+	 * @param origin the person who changed the mode
+	 * @param mode the new mode
+	 */
+	void onMode(std::shared_ptr<Server> server, std::string origin, std::string mode);
+
+	/**
+	 * On names listing.
+	 *
+	 * @param server the server
+	 * @param channel the channel
+	 * @param list the list of nicknames
+	 */
+	void onNames(std::shared_ptr<Server> server, std::string channel, std::vector<std::string> list);
+
+	/**
+	 * On nick change.
+	 *
+	 * @param server the server
+	 * @param origin the user that changed its nickname
+	 * @param nick the new nickname
+	 */
+	void onNick(std::shared_ptr<Server> server, std::string origin, std::string nick);
+
+	/**
+	 * On user notice.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the notice
+	 * @param notice the notice
+	 */
+	void onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice);
+
+	/**
+	 * On part.
+	 *
+	 * @param server the server
+	 * @param origin the user who left
+	 * @param channel the channel
+	 * @param reason the optional reason
+	 */
+	void onPart(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string reason);
+
+	/**
+	 * On user query.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the query
+	 * @param message the message
+	 */
+	void onQuery(std::shared_ptr<Server> server, std::string origin, std::string message);
+
+	/**
+	 * On user query command.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the query
+	 * @param message the message
+	 */
+	void onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message);
+
+	/**
+	 * On reload.
+	 */
+	void onReload();
+
+	/**
+	 * On topic change.
+	 *
+	 * @param server the server
+	 * @param origin the user who sent the topic
+	 * @param channel the channel
+	 * @param topic the new topic
+	 */
+	void onTopic(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string topic);
+
+	/**
+	 * On unload.
+	 */
+	void onUnload();
+
+	/**
+	 * On whois information.
+	 *
+	 * @param server the server
+	 * @param info the info
+	 */
+	void onWhois(std::shared_ptr<Server> server, ServerWhois info);
+};
+
+namespace js {
+
+template <>
+class TypeInfo<PluginInfo> {
+public:
+	static void push(Context &ctx, const PluginInfo &info);
+};
+
+} // !js
+
+} // !irccd
+
+#endif // !_IRCCD_PLUGIN_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/private/CMakeSources.cmake	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,21 @@
+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
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/private/connection.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/connection.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,228 @@
+/*
+ * 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:
+	/**
+	 * 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/private/directory.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/directory.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/elapsed-timer.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/elapsed-timer.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/filesystem.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/filesystem.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/ini.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/ini.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/private/signals.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/sockets.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/sockets.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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() : SSLv3_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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/private/xdg.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -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/private/xdg.h	Thu Mar 24 14:07:30 2016 +0100
@@ -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_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/rule.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,147 @@
+/*
+ * rule.cpp -- rule for server and channels
+ *
+ * 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 <stdexcept>
+
+#include "logger.h"
+#include "rule.h"
+#include "util.h"
+
+using namespace std;
+
+namespace {
+
+const std::unordered_set<std::string> validEvents{
+	"onChannelMode"
+	"onChannelNotice",
+	"onCommand",
+	"onConnect",
+	"onInvite",
+	"onJoin",
+	"onKick",
+	"onMessage",
+	"onMode",
+	"onNames",
+	"onNick",
+	"onNotice",
+	"onPart",
+	"onQuery",
+	"onQueryCommand",
+	"onTopic",
+	"onWhois"
+};
+
+} // !namespace
+
+namespace irccd {
+
+bool Rule::solve(const std::vector<Rule> &rules,
+		 const std::string &server,
+		 const std::string &channel,
+		 const std::string &origin,
+		 const std::string &plugin,
+		 const std::string &event) noexcept
+{
+	bool result = true;
+
+	log::debug() << "rule: solving for:\n"
+		     << "  server: " << server << "\n"
+		     << "  channel: " << channel << "\n"
+		     << "  origin: " << origin << "\n"
+		     << "  plugin: " << plugin << "\n"
+		     << "  event: " << event << std::endl;
+
+	int i = 0;
+	for (const Rule &rule : rules) {
+		log::debug() << "  candidate " << i++ << ":\n"
+			     << "    servers: " << util::join(rule.m_servers.begin(), rule.m_servers.end()) << "\n"
+			     << "    channels: " << util::join(rule.m_channels.begin(), rule.m_channels.end()) << "\n"
+			     << "    origins: " << util::join(rule.m_origins.begin(), rule.m_origins.end()) << "\n"
+			     << "    plugins: " << util::join(rule.m_plugins.begin(), rule.m_plugins.end()) << "\n"
+			     << "    events: " << util::join(rule.m_events.begin(), rule.m_events.end()) << "\n"
+			     << "    action: " << ((rule.m_action == RuleAction::Accept) ? "accept" : "drop") << std::endl;
+
+		if (rule.match(server, channel, origin, plugin, event))
+			result = rule.action() == RuleAction::Accept;
+	}
+
+	return result;
+}
+
+bool Rule::matchMap(const RuleSet &map, const std::string &value) const noexcept
+{
+	return value.empty() || map.empty() || map.count(value) == 1;
+}
+
+Rule::Rule(RuleSet servers, RuleSet channels, RuleSet origins, RuleSet plugins, RuleSet events, RuleAction action)
+	: m_servers(std::move(servers))
+	, m_channels(std::move(channels))
+	, m_origins(std::move(origins))
+	, m_plugins(std::move(plugins))
+	, m_events(std::move(events))
+	, m_action(action)
+{
+	for (const std::string &n : m_events)
+		if (validEvents.count(n) == 0)
+			throw std::invalid_argument(n + " is not a valid event name");
+}
+
+bool Rule::match(const std::string &server,
+		 const std::string &channel,
+		 const std::string &nick,
+		 const std::string &plugin,
+		 const std::string &event) const noexcept
+{
+	return matchMap(m_servers, server) &&
+	       matchMap(m_channels, channel) &&
+	       matchMap(m_origins, nick) &&
+	       matchMap(m_plugins, plugin) &&
+	       matchMap(m_events, event);
+}
+
+RuleAction Rule::action() const noexcept
+{
+	return m_action;
+}
+
+const RuleSet &Rule::servers() const noexcept
+{
+	return m_servers;
+}
+
+const RuleSet &Rule::channels() const noexcept
+{
+	return m_channels;
+}
+
+const RuleSet &Rule::origins() const noexcept
+{
+	return m_origins;
+}
+
+const RuleSet &Rule::plugins() const noexcept
+{
+	return m_plugins;
+}
+
+const RuleSet &Rule::events() const noexcept
+{
+	return m_events;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/rule.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,166 @@
+/*
+ * rule.h -- rule for server and channels
+ *
+ * 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_RULE_H_
+#define _IRCCD_RULE_H_
+
+/**
+ * @file Rule.h
+ * @brief Rule description
+ */
+
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * List of criterias.
+ */
+using RuleSet = std::unordered_set<std::string>;
+
+/**
+ * @enum RuleAction
+ * @brief Rule action
+ */
+enum class RuleAction {
+	Accept,			//!< The event is accepted (default)
+	Drop			//!< The event is dropped
+};
+
+/**
+ * @class Rule
+ * @brief Manage rule to activate or deactive events.
+ */
+class Rule final {
+private:
+	RuleSet m_servers;
+	RuleSet m_channels;
+	RuleSet m_origins;
+	RuleSet m_plugins;
+	RuleSet m_events;
+	RuleAction m_action{RuleAction::Accept};
+
+	/*
+	 * Check if a map contains the value and return true if it is
+	 * or return true if value is empty (which means applicable).
+	 */
+	bool matchMap(const RuleSet &map, const std::string &value) const noexcept;
+
+public:
+	/**
+	 * Resolve the action to execute with the specified list of rules.
+	 *
+	 * @param rules the list of rules
+	 * @param server the server name
+	 * @param channel the channel name
+	 * @param origin the origin
+	 * @param plugin the plugin name
+	 * @param event the event name (e.g onKick)
+	 * @return true if the plugin must be called
+	 */
+	static bool solve(const std::vector<Rule> &rules,
+			  const std::string &server,
+			  const std::string &channel,
+			  const std::string &origin,
+			  const std::string &plugin,
+			  const std::string &event) noexcept;
+
+	/**
+	 * Rule constructor.
+	 *
+	 * @param servers the server list
+	 * @param channels the channels
+	 * @param nicknames the nicknames
+	 * @param plugins the plugins
+	 * @param events the events
+	 * @param action the rule action
+	 * @throw std::invalid_argument if events are invalid
+	 */
+	Rule(RuleSet servers = RuleSet{},
+	     RuleSet channels = RuleSet{},
+	     RuleSet nicknames = RuleSet{},
+	     RuleSet plugins = RuleSet{},
+	     RuleSet events = RuleSet{},
+	     RuleAction action = RuleAction::Accept);
+
+	/**
+	 * Check if that rule apply for the given criterias.
+	 *
+	 * @param server the server
+	 * @param channel the channel
+	 * @param nick the origin
+	 * @param plugin the plugin
+	 * @param event the event
+	 * @return true if match
+	 */
+	bool match(const std::string &server,
+		   const std::string &channel,
+		   const std::string &nick,
+		   const std::string &plugin,
+		   const std::string &event) const noexcept;
+
+	/**
+	 * Get the action.
+	 *
+	 * @return the action
+	 */
+	RuleAction action() const noexcept;
+
+	/**
+	 * Get the servers.
+	 *
+	 * @return the servers
+	 */
+	const RuleSet &servers() const noexcept;
+
+	/**
+	 * Get the channels.
+	 *
+	 * @return the channels
+	 */
+	const RuleSet &channels() const noexcept;
+
+	/**
+	 * Get the origins.
+	 *
+	 * @return the origins
+	 */
+	const RuleSet &origins() const noexcept;
+
+	/**
+	 * Get the plugins.
+	 *
+	 * @return the plugins
+	 */
+	const RuleSet &plugins() const noexcept;
+
+	/**
+	 * Get the events.
+	 *
+	 * @return the events
+	 */
+	const RuleSet &events() const noexcept;
+};
+
+} // !irccd
+
+#endif // !_IRCCD_RULE_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/server-state.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,184 @@
+/*
+ * server-state.cpp -- server current state
+ *
+ * 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 <irccd-config.h>
+
+#if !defined(_WIN32)
+#  include <sys/types.h>
+#  include <netinet/in.h>
+#  include <arpa/nameser.h>
+#  include <resolv.h>
+#endif
+
+#include "server-state.h"
+#include "server.h"
+
+namespace irccd {
+
+bool ServerState::connect(Server &server)
+{
+	const ServerInfo &info = server.info();
+	const ServerIdentity &identity = server.identity();
+	const char *password = info.password.empty() ? nullptr : info.password.c_str();
+	std::string host = info.host;
+	int code;
+
+	/* libircclient requires # for SSL connection */
+#if defined(WITH_SSL)
+	if (info.flags & ServerInfo::Ssl)
+		host.insert(0, 1, '#');
+	if (!(info.flags & ServerInfo::SslVerify))
+		irc_option_set(server.session(), LIBIRC_OPTION_SSL_NO_VERIFY);
+#endif
+
+	if (info.flags & ServerInfo::Ipv6) {
+		code = irc_connect6(server.session(), host.c_str(), info.port, password,
+				    identity.nickname.c_str(),
+				    identity.username.c_str(),
+				    identity.realname.c_str());
+	} else {
+		code = irc_connect(server.session(), host.c_str(), info.port, password,
+				   identity.nickname.c_str(),
+				   identity.username.c_str(),
+				   identity.realname.c_str());
+	}
+
+	return code == 0;
+}
+
+void ServerState::prepareConnected(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
+{
+	if (!irc_is_connected(server.session())) {
+		const ServerSettings &settings = server.settings();
+
+		log::warning() << "server " << server.info().name << ": disconnected" << std::endl;
+
+		if (settings.recotimeout > 0) {
+			log::warning() << "server " << server.info().name << ": retrying in "
+					  << settings.recotimeout << " seconds" << std::endl;
+		}
+
+		server.next(ServerState::Disconnected);
+	} else {
+		irc_add_select_descriptors(server.session(), &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
+	}
+}
+
+void ServerState::prepareConnecting(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
+{
+	/*
+	 * The connect function will either fail if the hostname wasn't resolved
+	 * or if any of the internal functions fail.
+	 *
+	 * It returns success if the connection was successful but it does not
+	 * mean that connection is established.
+	 *
+	 * Because this function will be called repeatidly, the connection was started and we're still not
+	 * connected in the specified timeout time, we mark the server as disconnected.
+	 *
+	 * Otherwise, the libircclient event_connect will change the state.
+	 */
+	const ServerInfo &info = server.info();
+
+	if (m_started) {
+		const ServerSettings &settings = server.settings();
+
+		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
+			log::warning() << "server " << info.name << ": timeout while connecting" << std::endl;
+			server.next(ServerState::Disconnected);
+		} else if (!irc_is_connected(server.session())) {
+			log::warning() << "server " << info.name << ": error while connecting: ";
+			log::warning() << irc_strerror(irc_errno(server.session())) << std::endl;
+
+			if (settings.recotries != 0)
+				log::warning() << "server " << info.name << ": retrying in " << settings.recotimeout << " seconds" << std::endl;
+
+			server.next(ServerState::Disconnected);
+		} else {
+			irc_add_select_descriptors(server.session(), &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
+		}
+	} else {
+		/*
+		 * This is needed if irccd is started before DHCP or if
+		 * DNS cache is outdated.
+		 *
+		 * For more information see bug #190.
+		 */
+#if !defined(_WIN32)
+		(void)res_init();
+#endif
+		log::info() << "server " << info.name << ": trying to connect to " << info.host << ", port " << info.port << std::endl;
+
+		if (!connect(server)) {
+			log::warning() << "server " << info.name << ": disconnected while connecting: ";
+			log::warning() << irc_strerror(irc_errno(server.session())) << std::endl;
+			server.next(ServerState::Disconnected);
+		} else {
+			m_started = true;
+		}
+	}
+}
+
+void ServerState::prepareDisconnected(Server &server, fd_set &, fd_set &, net::Handle &)
+{
+	const ServerInfo &info = server.info();
+	ServerSettings &settings = server.settings();
+
+	if (settings.recotries == 0) {
+		log::warning() << "server " << info.name << ": reconnection disabled, skipping" << std::endl;
+		server.onDie();
+	} else if (settings.recotries > 0 && settings.recocurrent > settings.recotries) {
+		log::warning() << "server " << info.name << ": giving up" << std::endl;
+		server.onDie();
+	} else {
+		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
+			irc_disconnect(server.session());
+
+			settings.recocurrent ++;
+			server.next(ServerState::Connecting);
+		}
+	}
+}
+
+ServerState::ServerState(Type type)
+	: m_type(type)
+{
+	assert(static_cast<int>(m_type) >= static_cast<int>(ServerState::Undefined));
+	assert(static_cast<int>(m_type) <= static_cast<int>(ServerState::Disconnected));
+}
+
+void ServerState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
+{
+	switch (m_type) {
+	case Connecting:
+		prepareConnecting(server, setinput, setoutput, maxfd);
+		break;
+	case Connected:
+		prepareConnected(server, setinput, setoutput, maxfd);
+		break;
+	case Disconnected:
+		prepareDisconnected(server, setinput, setoutput, maxfd);
+		break;
+	default:
+		break;
+	}
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/server-state.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,77 @@
+/*
+ * server-state.h -- server current state
+ *
+ * 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_SERVER_STATE_H_
+#define _IRCCD_SERVER_STATE_H_
+
+#include <irccd-config.h>
+
+#include <irccd/private/elapsed-timer.h>
+#include <irccd/private/sockets.h>
+
+namespace irccd {
+
+class Server;
+
+/**
+ * @class ServerState
+ * @brief Server current state.
+ */
+class ServerState {
+public:
+	/**
+	 * @enum Type
+	 * @brief Server state
+	 */
+	enum Type {
+		Undefined,	//!< Not defined yet
+		Connecting,	//!< Connecting to the server
+		Connected,	//!< Connected and running
+		Disconnected,	//!< Disconnected and waiting before retrying
+	};
+
+private:
+	Type m_type;
+
+	/* For ServerState::Connecting */
+	bool m_started{false};
+	ElapsedTimer m_timer;
+
+	/* Private helpers */
+	bool connect(Server &server);
+
+	/* Different preparation */
+	void prepareConnected(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
+	void prepareConnecting(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
+	void prepareDisconnected(Server &, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
+
+public:
+	ServerState(Type type);
+
+	void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd);
+
+	inline Type type() const noexcept
+	{
+		return m_type;
+	}
+};
+
+} // !irccd
+
+#endif // !_IRCCD_SERVER_STATE_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/server.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,448 @@
+/*
+ * server.cpp -- an IRC server
+ *
+ * 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 <cerrno>
+#include <cstring>
+#include <stdexcept>
+
+#include <libirc_rfcnumeric.h>
+
+#include "logger.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
+{
+	char target[32]{0};
+
+	irc_target_get_nick(nick.c_str(), target, sizeof (target));
+
+	return m_identity.nickname == target;
+}
+
+void Server::extractPrefixes(const std::string &line)
+{
+	std::pair<char, char> table[16];
+	std::string buf = line.substr(7);
+
+	for (int i = 0; i < 16; ++i)
+		table[i] = std::make_pair(-1, -1);
+
+	int j = 0;
+	bool readModes = true;
+	for (size_t i = 0; i < buf.size(); ++i) {
+		if (buf[i] == '(')
+			continue;
+		if (buf[i] == ')') {
+			j = 0;
+			readModes = false;
+			continue;
+		}
+
+		if (readModes)
+			table[j++].first = buf[i];
+		else
+			table[j++].second = buf[i];
+	}
+
+	// Put these as a map of mode to prefix
+	for (int i = 0; i < 16; ++i) {
+		auto key = static_cast<ServerChanMode>(table[i].first);
+		auto value = table[i].second;
+
+		m_info.modes.emplace(key, value);
+	}
+}
+
+std::string Server::cleanPrefix(std::string nickname) const noexcept
+{
+	if (nickname.length() > 0)
+		for (const auto &pair : m_info.modes)
+			if (nickname[0] == pair.second)
+				nickname.erase(0, 1);
+
+	return nickname;
+}
+
+void Server::handleConnect(const char *, const char **) noexcept
+{
+	/* Reset the number of tried reconnection. */
+	m_settings.recocurrent = 0;
+
+	/* Don't forget to change state and notify. */
+	next(ServerState::Connected);
+	onConnect();
+
+	/* Auto join listed channels. */
+	for (const ServerChannel &channel : m_settings.channels) {
+		log::info() << "server " << m_info.name << ": auto joining " << channel.name << std::endl;
+		join(channel.name, channel.password);
+	}
+}
+
+void Server::handleChannel(const char *orig, const char **params) noexcept
+{
+	onMessage(strify(orig), strify(params[0]), strify(params[1]));
+}
+
+void Server::handleChannelMode(const char *orig, const char **params) noexcept
+{
+	onChannelMode(strify(orig), strify(params[0]), strify(params[1]), strify(params[2]));
+}
+
+void Server::handleChannelNotice(const char *orig, const char **params) noexcept
+{
+	onChannelNotice(strify(orig), strify(params[0]), strify(params[1]));
+}
+
+void Server::handleCtcpAction(const char *orig, const char **params) noexcept
+{
+	onMe(strify(orig), strify(params[0]), strify(params[1]));
+}
+
+void Server::handleInvite(const char *orig, const char **params) noexcept
+{
+	/* If joininvite is set, join the channel */
+	if ((m_settings.flags & ServerSettings::JoinInvite) && isSelf(strify(params[0])))
+		join(strify(params[1]));
+
+	/*
+	 * The libircclient says that invite contains the target nickname, it's quite
+	 * uncommon to need it so it is passed as the last argument to be
+	 * optional in the plugin.
+	 */
+	onInvite(strify(orig), strify(params[1]), strify(params[0]));
+}
+
+void Server::handleJoin(const char *orig, const char **params) noexcept
+{
+	onJoin(strify(orig), strify(params[0]));
+}
+
+void Server::handleKick(const char *orig, const char **params) noexcept
+{
+	/* Rejoin the channel if the option has been set and I was kicked. */
+	if ((m_settings.flags & ServerSettings::AutoRejoin) && isSelf(strify(params[1])))
+		join(strify(params[0]));
+
+	onKick(strify(orig), strify(params[0]), strify(params[1]), strify(params[2]));
+}
+
+void Server::handleMode(const char *orig, const char **params) noexcept
+{
+	onMode(strify(orig), strify(params[1]));
+}
+
+void Server::handleNick(const char *orig, const char **params) noexcept
+{
+	/* Update our nickname. */
+	if (isSelf(strify(orig)))
+		m_identity.nickname = strify(params[0]);
+
+	onNick(strify(orig), strify(params[0]));
+}
+
+void Server::handleNotice(const char *orig, const char **params) noexcept
+{
+	/*
+	 * As for handleInvite, the notice provides the target nickname, we discard it.
+	 */
+	onNotice(strify(orig), strify(params[1]));
+}
+
+void Server::handleNumeric(unsigned int event, const char **params, unsigned int c) noexcept
+{
+	if (event == LIBIRC_RFC_RPL_NAMREPLY) {
+		/*
+		 * Called multiple times to list clients on a channel.
+		 *
+		 * params[0] == originator
+		 * params[1] == '='
+		 * params[2] == channel
+		 * params[3] == list of users with their prefixes
+		 *
+		 * IDEA for the future: maybe give the appropriate mode as a second parameter in onNames.
+		 */
+		if (c < 4 || params[2] == nullptr || params[3] == nullptr)
+			return;
+
+		std::vector<std::string> users = util::split(params[3], " \t");
+
+		/* The listing may add some prefixes, remove them if needed */
+		for (std::string u : users)
+			m_namesMap[params[2]].insert(cleanPrefix(u));
+	} else if (event == LIBIRC_RFC_RPL_ENDOFNAMES) {
+		/*
+		 * Called when end of name listing has finished on a channel.
+		 *
+		 * params[0] == originator
+		 * params[1] == channel
+		 * params[2] == End of NAMES list
+		 */
+
+		if (c < 3 || params[1] == nullptr)
+			return;
+
+		auto it = m_namesMap.find(params[1]);
+		if (it != m_namesMap.end()) {
+			onNames(params[1], it->second);
+
+			/* Don't forget to remove the list */
+			m_namesMap.erase(it);
+		}
+	} else if (event == LIBIRC_RFC_RPL_WHOISUSER) {
+		/*
+		 * Called when whois information has been partially received.
+		 *
+		 * params[0] == originator
+		 * params[1] == nickname
+		 * params[2] == username
+		 * params[3] == host
+		 * params[4] == * (no idea what is that)
+		 * params[5] == realname
+		 */
+		if (c < 6 || !params[1] || !params[2] || !params[3] || !params[5])
+			return;
+
+		ServerWhois info;
+
+		info.nick = strify(params[1]);
+		info.user = strify(params[2]);
+		info.host = strify(params[3]);
+		info.realname = strify(params[5]);
+
+		m_whoisMap.emplace(info.nick, info);
+	} else if (event == LIBIRC_RFC_RPL_WHOISCHANNELS) {
+		/*
+		 * Called when we have received channels for one user.
+		 *
+		 * params[0] == originator
+		 * params[1] == nickname
+		 * params[2] == list of channels with their prefixes
+		 */
+		if (c < 3 || !params[1] || !params[2])
+			return;
+
+		auto it = m_whoisMap.find(params[1]);
+		if (it != m_whoisMap.end()) {
+			std::vector<std::string> channels = util::split(params[2], " \t");
+
+			/* Clean their prefixes */
+			for (auto &s : channels)
+				s = cleanPrefix(s);
+
+			/* Insert */
+			it->second.channels = std::move(channels);
+		}
+	} else if (event == LIBIRC_RFC_RPL_ENDOFWHOIS) {
+		/*
+		 * Called when whois is finished.
+		 *
+		 * params[0] == originator
+		 * params[1] == nickname
+		 * params[2] == End of WHOIS list
+		 */
+
+		auto it = m_whoisMap.find(params[1]);
+		if (it != m_whoisMap.end()) {
+			onWhois(it->second);
+
+			/* Don't forget to remove */
+			m_whoisMap.erase(it);
+		}
+	} else if (event == /* RPL_BOUNCE */ 5) {
+		/*
+		 * The event 5 is usually RPL_BOUNCE, but we always see it as ISUPPORT.
+		 */
+		for (unsigned int i = 0; i < c; ++i) {
+			if (strncmp(params[i], "PREFIX", 6) == 0) {
+				extractPrefixes(params[i]);
+				break;
+			}
+		}
+	}
+}
+
+void Server::handlePart(const char *orig, const char **params) noexcept
+{
+	onPart(strify(orig), strify(params[0]), strify(params[1]));
+}
+
+void Server::handleQuery(const char *orig, const char **params) noexcept
+{
+	onQuery(strify(orig), strify(params[1]));
+}
+
+void Server::handleTopic(const char *orig, const char **params) noexcept
+{
+	onTopic(strify(orig), strify(params[0]), strify(params[1]));
+}
+
+ServerChannel Server::splitChannel(const std::string &value)
+{
+	auto pos = value.find(':');
+
+	if (pos != std::string::npos)
+		return ServerChannel{value.substr(0, pos), value.substr(pos + 1)};
+
+	return ServerChannel{value, ""};
+}
+
+Server::Server(ServerInfo info, ServerIdentity identity, ServerSettings settings)
+	: m_info(std::move(info))
+	, m_settings(std::move(settings))
+	, m_identity(std::move(identity))
+	, m_session(nullptr, nullptr)
+	, m_state(ServerState::Connecting)
+	, m_next(ServerState::Undefined)
+{
+	irc_callbacks_t callbacks;
+
+	/*
+	 * GCC 4.9.2 triggers some missing-field-initializers warnings when
+	 * using uniform initialization so use a std::memset as a workaround.
+	 */
+	std::memset(&callbacks, 0, sizeof (irc_callbacks_t));
+
+	/*
+	 * Convert the raw pointer functions from libircclient to Server member
+	 * function.
+	 *
+	 * While doing this, discard useless arguments.
+	 */
+	callbacks.event_channel = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleChannel(orig, params);
+	};
+	callbacks.event_channel_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleChannelNotice(orig, params);
+	};
+	callbacks.event_connect = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleConnect(orig, params);
+	};
+	callbacks.event_ctcp_action = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleCtcpAction(orig, params);
+	};
+	callbacks.event_invite = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleInvite(orig, params);
+	};
+	callbacks.event_join = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleJoin(orig, params);
+	};
+	callbacks.event_kick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleKick(orig, params);
+	};
+	callbacks.event_mode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleChannelMode(orig, params);
+	};
+	callbacks.event_nick = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleNick(orig, params);
+	};
+	callbacks.event_notice = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleNotice(orig, params);
+	};
+	callbacks.event_numeric = [] (irc_session_t *session, unsigned int event, const char *, const char **params, unsigned int count) {
+		static_cast<Server *>(irc_get_ctx(session))->handleNumeric(event, params, count);
+	};
+	callbacks.event_part = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handlePart(orig, params);
+	};
+	callbacks.event_privmsg = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleQuery(orig, params);
+	};
+	callbacks.event_topic = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleTopic(orig, params);
+	};
+	callbacks.event_umode = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
+		static_cast<Server *>(irc_get_ctx(session))->handleMode(orig, params);
+	};
+
+	m_session = Session{irc_create_session(&callbacks), irc_destroy_session};
+
+	/* Save this to the session */
+	irc_set_ctx(m_session.get(), this);
+	irc_set_ctcp_version(m_session.get(), m_identity.ctcpversion.c_str());
+}
+
+Server::~Server()
+{
+	irc_disconnect(m_session.get());
+}
+
+void Server::update() noexcept
+{
+	if (m_next.type() != ServerState::Undefined) {
+		log::debug() << "server " << m_info.name << ": switching to state ";
+
+		switch (m_next.type()) {
+		case ServerState::Connecting:
+			log::debug() << "\"Connecting\"" << std::endl;
+			break;
+		case ServerState::Connected:
+			log::debug() << "\"Connected\"" << std::endl;
+			break;
+		case ServerState::Disconnected:
+			log::debug() << "\"Disconnected\"" << std::endl;
+			break;
+		default:
+			break;
+		}
+
+		m_state = std::move(m_next);
+		m_next = ServerState::Undefined;
+	}
+}
+
+void Server::sync(fd_set &setinput, fd_set &setoutput) noexcept
+{
+	/*
+	 * 1. Send maximum of command possible if available for write */
+	/*
+	 * Break on the first failure to avoid changing the order of the
+	 * commands if any of them fails.
+	 */
+	bool done = false;
+
+	while (!m_queue.empty() && !done) {
+		if (m_queue.front()())
+			m_queue.pop();
+		else
+			done = true;
+	}
+
+	/* 2. Read data */
+	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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/server.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,874 @@
+/*
+ * server.h -- an IRC server
+ *
+ * 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_SERVER_H_
+#define _IRCCD_SERVER_H_
+
+#include <cstdint>
+#include <functional>
+#include <map>
+#include <memory>
+#include <queue>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <libircclient.h>
+
+#include <irccd-config.h>
+
+#include <irccd/private/signals.h>
+
+#include "logger.h"
+#include "server-state.h"
+
+namespace irccd {
+
+namespace js {
+
+class Context;
+
+} // !js
+
+/**
+ * @class ServerIdentity
+ * @brief Identity to use when connecting
+ */
+class ServerIdentity {
+public:
+	std::string name{"irccd"};			//!< identity name
+	std::string nickname{"irccd"};			//!< nickname to show
+	std::string username{"irccd"};			//!< username to use for connection
+	std::string realname{"IRC Client Daemon"};	//!< the full real name
+	std::string ctcpversion{"IRC Client Daemon"};	//!< the CTCP version to define
+};
+
+/**
+ * @class ServerChannel
+ * @brief A channel to join with an optional password
+ */
+class ServerChannel {
+public:
+	std::string name;				//!< the channel to join
+	std::string password;				//!< the optional password
+};
+
+/**
+ * List of channels.
+ */
+using ServerChannels = std::vector<ServerChannel>;
+
+/**
+ * @enum ServerChanMode
+ * @brief Prefixes for nicknames
+ */
+enum class ServerChanMode {
+	Creator		= 'O',			//!< Channel creator
+	HalfOperator	= 'h',			//!< Half operator
+	Operator	= 'o',			//!< Channel operator
+	Protection	= 'a',			//!< Unkillable
+	Voiced		= 'v'			//!< Voice power
+};
+
+/**
+ * @class ServerWhois
+ * @brief Describe a whois information
+ *
+ * This is provided when whois command was requested.
+ */
+class ServerWhois {
+public:
+	std::string nick;			//!< user's nickname
+	std::string user;			//!< user's user
+	std::string host;			//!< hostname
+	std::string realname;			//!< realname
+	std::vector<std::string> channels;	//!< the channels where the user is
+};
+
+/**
+ * @class ServerInfo
+ * @brief Server information
+ *
+ * This class contains everything needed to connect to a server.
+ */
+class ServerInfo {
+public:
+	enum {
+		Ipv6		= (1 << 0),	//!< Connect using IPv6
+		Ssl		= (1 << 1),	//!< Use SSL
+		SslVerify	= (1 << 2)	//!< Verify SSL
+	};
+
+	std::string name;			//!< Server's name
+	std::string host;			//!< Hostname
+	std::string password;			//!< Optional server password
+	std::uint16_t port{6667};		//!< Server's port
+	std::uint8_t flags{0};			//!< Optional flags
+	std::map<ServerChanMode, char> modes;	//!< IRC modes (e.g. @~)
+};
+
+/**
+ * @class ServerSettings
+ * @brief Contains settings to tweak the server
+ *
+ * This class contains additional settings that tweaks the
+ * server operations.
+ */
+class ServerSettings {
+public:
+	enum {
+		AutoRejoin	= (1 << 0),	//!< Auto rejoin a channel after being kicked
+		JoinInvite	= (1 << 1)	//!< Join a channel on invitation
+	};
+
+	ServerChannels channels;		//!< List of channel to join
+	std::string command{"!"};		//!< The command character to trigger plugin command
+	std::int8_t recotries{-1};		//!< Number of tries to reconnect before giving up
+	std::uint16_t recotimeout{30};		//!< Number of seconds to wait before trying to connect
+	std::uint8_t flags{0};			//!< Optional flags
+
+	/* Private */
+	std::int8_t recocurrent{1};		//!< number of tries tested
+};
+
+/**
+ * Deferred command to send to the server.
+ *
+ * If the command returns true, it has been correctly buffered for outgoing
+ * and removed from the queue.
+ */
+using ServerCommand = std::function<bool ()>;
+
+/**
+ * @class Server
+ * @brief The class that connect to a IRC server
+ *
+ * The server is a class that stores callbacks which will be called on IRC events. It is the lowest part of the
+ * connection to a server, it can be used directly by the user to connect to a server.
+ *
+ * The server has several signals that will be emitted when data has arrived.
+ *
+ * When adding a server to the irccd instance using Irccd::addServer, these signals are connected to generate
+ * events that will be dispatched to the plugins and to the transports.
+ *
+ * Note: the server is set in non blocking mode, commands are placed in a queue and sent when only when they are ready.
+ */
+class Server {
+public:
+	/**
+	 * Signal: onChannelMode
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone changed the channel mode.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel
+	 * - the mode
+	 * - the optional mode argument
+	 */
+	Signal<std::string, std::string, std::string, std::string> onChannelMode;
+
+	/**
+	 * Signal: onChannelNotice
+	 * ------------------------------------------------
+	 *
+	 * Triggered when a notice has been sent on a channel.
+	 *
+	 * Arguments:
+	 * - the origin (the nickname who has sent the notice)
+	 * - the channel name
+	 * - the notice message
+	 */
+	Signal<std::string, std::string, std::string> onChannelNotice;
+
+	/**
+	 * Signal: onConnect
+	 * ------------------------------------------------
+	 *
+	 * Triggered when the server is successfully connected.
+	 */
+	Signal<> onConnect;
+
+	/**
+	 * Signal: onDie
+	 * ----------------------------------------------------------
+	 *
+	 * The server is dead.
+	 */
+	Signal<> onDie;
+
+	/**
+	 * Signal: onInvite
+	 * ------------------------------------------------
+	 *
+	 * Triggered when an invite has been sent to you (the bot).
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel
+	 * - your nickname
+	 */
+	Signal<std::string, std::string, std::string> onInvite;
+
+	/**
+	 * Signal: onJoin
+	 * ------------------------------------------------
+	 *
+	 * Triggered when a user has joined the channel, it also includes you.
+	 *
+	 * Arguments:
+	 * - the origin (may be you)
+	 * - the channel
+	 */
+	Signal<std::string, std::string> onJoin;
+
+	/**
+	 * Signal: onKick
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone has been kicked from a channel.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel
+	 * - the target who has been kicked
+	 * - the optional reason
+	 */
+	Signal<std::string, std::string, std::string, std::string> onKick;
+
+	/**
+	 * ServerEvent: onMessage
+	 * ------------------------------------------------
+	 *
+	 * Triggered when a message on a channel has been sent.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel
+	 * - the message
+	 */
+	Signal<std::string, std::string, std::string> onMessage;
+
+	/**
+	 * Signal: onMe
+	 * ------------------------------------------------
+	 *
+	 * Triggered on a CTCP Action.
+	 *
+	 * This is both used in a channel and in a private message so the target
+	 * may be a channel or your nickname.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the target
+	 * - the message
+	 */
+	Signal<std::string, std::string, std::string> onMe;
+
+	/**
+	 * Signal: onMode
+	 * ------------------------------------------------
+	 *
+	 * Triggered when the server changed your user mode.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the mode (e.g +i)
+	 */
+	Signal<std::string, std::string> onMode;
+
+	/**
+	 * Signal: onNames
+	 * ------------------------------------------------
+	 *
+	 * Triggered when names listing has finished on a channel.
+	 *
+	 * Arguments:
+	 * - the channel
+	 * - the ordered list of names
+	 */
+	Signal<std::string, std::set<std::string>> onNames;
+
+	/**
+	 * Signal: onNick
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone changed its nickname, it also includes you.
+	 *
+	 * Arguments:
+	 * - the old nickname (may be you)
+	 * - the new nickname
+	 */
+	Signal<std::string, std::string> onNick;
+
+	/**
+	 * Signal: onNotice
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone has sent a notice to you.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the notice message
+	 */
+	Signal<std::string, std::string> onNotice;
+
+	/**
+	 * Signal: onPart
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone has left the channel.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel that the nickname has left
+	 * - the optional reason
+	 */
+	Signal<std::string, std::string, std::string> onPart;
+
+	/**
+	 * Signal: onQuery
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone has sent you a private message.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the message
+	 */
+	Signal<std::string, std::string> onQuery;
+
+	/**
+	 * Signal: onTopic
+	 * ------------------------------------------------
+	 *
+	 * Triggered when someone changed the channel topic.
+	 *
+	 * Arguments:
+	 * - the origin
+	 * - the channel
+	 * - the new topic
+	 */
+	Signal<std::string, std::string, std::string> onTopic;
+
+	/*
+	 * Signal: onWhois
+	 * ------------------------------------------------
+	 *
+	 * Triggered when whois information has been received.
+	 *
+	 * Arguments:
+	 * - the whois object
+	 */
+	Signal<ServerWhois> onWhois;
+
+private:
+	using Session = std::unique_ptr<irc_session_t, void (*)(irc_session_t *)>;
+	using Queue = std::queue<ServerCommand>;
+
+	/**
+	 * List of NAMES being built.
+	 */
+	using NamesMap = std::unordered_map<std::string, std::set<std::string>>;
+
+	/**
+	 * List of WHOIS being built.
+	 */
+	using WhoisMap	= std::unordered_map<std::string, ServerWhois>;
+
+private:
+	ServerInfo m_info;
+	ServerSettings m_settings;
+	ServerIdentity m_identity;
+	Session m_session;
+	ServerState m_state;
+	ServerState m_next;
+	Queue m_queue;
+
+	/*
+	 * The names map is being built by a successive call to handleNumeric so we need to store a temporary
+	 * map by channels to list of names. Then, when we receive the end of names listing, we remove the
+	 * temporary set of names and calls the appropriate signal.
+	 */
+	NamesMap m_namesMap;
+	WhoisMap m_whoisMap;
+
+	bool isSelf(const std::string &nick) const noexcept;
+	void extractPrefixes(const std::string &line);
+	std::string cleanPrefix(std::string nickname) const noexcept;
+
+	inline std::string strify(const char *s)
+	{
+		return (s == nullptr) ? "" : std::string(s);
+	}
+
+	void handleChannel(const char *, const char **) noexcept;
+	void handleChannelMode(const char *, const char **) noexcept;
+	void handleChannelNotice(const char *, const char **) noexcept;
+	void handleConnect(const char *, const char **) noexcept;
+	void handleCtcpAction(const char *, const char **) noexcept;
+	void handleInvite(const char *, const char **) noexcept;
+	void handleJoin(const char *, const char **) noexcept;
+	void handleKick(const char *, const char **) noexcept;
+	void handleMode(const char *, const char **) noexcept;
+	void handleNick(const char *, const char **) noexcept;
+	void handleNotice(const char *, const char **) noexcept;
+	void handleNumeric(unsigned int, const char **, unsigned int) noexcept;
+	void handlePart(const char *, const char **) noexcept;
+	void handleQuery(const char *, const char **) noexcept;
+	void handleTopic(const char *, const char **) noexcept;
+
+public:
+	/**
+	 * Split a channel from the form channel:password into a ServerChannel object.
+	 *
+	 * @param value the value
+	 * @return a channel
+	 */
+	static ServerChannel splitChannel(const std::string &value);
+
+	/**
+	 * Construct a server.
+	 *
+	 * @param info the information
+	 * @param identity the identity
+	 * @param settings the settings
+	 */
+	Server(ServerInfo info, ServerIdentity identity = {}, ServerSettings settings = {});
+
+	/**
+	 * Destructor. Close the connection if needed.
+	 */
+	virtual ~Server();
+
+	/**
+	 * Set the next state to be used. This function is thread safe because
+	 * the server manager may set the next state to the current state.
+	 *
+	 * If the server is installed into the ServerManager, it is called
+	 * automatically.
+	 *
+	 * @param type the new state type
+	 * @warning Not thread-safe
+	 */
+	inline void next(ServerState::Type type)
+	{
+		m_next = ServerState(type);
+	}
+
+	/**
+	 * Switch to next state if it has.
+	 *
+	 * If the server is installed into irccd, it is called automatically.
+	 *
+	 * @warning Not thread-safe
+	 */
+	void update() noexcept;
+
+	/**
+	 * Request to disconnect. This function does not notify the
+	 * ServerService.
+	 *
+	 * @see Irccd::serverDisconnect
+	 * @note Thread-safe
+	 */
+	inline void disconnect() noexcept
+	{
+		using namespace std::placeholders;
+
+		irc_disconnect(m_session.get());
+		onDie();
+	}
+
+	/**
+	 * Asks for a reconnection. This function does not notify the
+	 * ServerService.
+	 *
+	 * @see Irccd::serverReconnect
+	 * @note Thread-safe
+	 */
+	inline void reconnect() noexcept
+	{
+		irc_disconnect(m_session.get());
+		next(ServerState::Type::Connecting);
+	}
+
+	/**
+	 * Flush the pending commands if possible. This function will send
+	 * as much as possible commands.
+	 *
+	 * If the server is installed into the ServerManager, it is called
+	 * automatically.
+	 *
+	 * @note Thread-safe
+	 */
+	void flush() noexcept;
+
+	/**
+	 * Prepare the IRC Session to the socket.
+	 *
+	 * If the server is installed into the ServerManager, it is called
+	 * automatically.
+	 *
+	 * @warning Not thread-safe
+	 */
+	inline void prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept
+	{
+		m_state.prepare(*this, setinput, setoutput, maxfd);
+	}
+
+	/**
+	 * Process incoming/outgoing data after selection.
+	 *
+	 * If the server is installed into the ServerManager, it is called
+	 * automatically.
+	 *
+	 * @param setinput
+	 * @param setoutput
+	 * @throw any exception that have been throw from user functions
+	 */
+	void sync(fd_set &setinput, fd_set &setoutput) noexcept;
+
+	/**
+	 * Get the server information.
+	 *
+	 * @warning This overload should not be used by the user, it is required to
+	 *          update the nickname.
+	 * @return the server information
+	 */
+	inline ServerInfo &info() noexcept
+	{
+		return m_info;
+	}
+
+	/**
+	 * Get the server information.
+	 *
+	 * @return the server information
+	 */
+	inline const ServerInfo &info() const noexcept
+	{
+		return m_info;
+	}
+
+	/**
+	 * Get the server settings.
+	 *
+	 * @warning This overload should not be used by the user, it is required to
+	 *          update the reconnection information.
+	 * @return the settings
+	 */
+	inline ServerSettings &settings() noexcept
+	{
+		return m_settings;
+	}
+
+	/**
+	 * Get the server settings.
+	 *
+	 * @return the settings
+	 */
+	inline const ServerSettings &settings() const noexcept
+	{
+		return m_settings;
+	}
+
+	/**
+	 * Get the identity.
+	 *
+	 * @return the identity
+	 */
+	inline ServerIdentity &identity() noexcept
+	{
+		return m_identity;
+	}
+
+	/**
+	 * Overloaded function
+	 *
+	 * @return the identity
+	 */
+	inline const ServerIdentity &identity() const noexcept
+	{
+		return m_identity;
+	}
+
+	/**
+	 * Get the current state identifier. Should not be used by user code.
+	 *
+	 * @note Thread-safe but the state may change just after the call
+	 */
+	inline ServerState::Type type() const noexcept
+	{
+		return m_state.type();
+	}
+
+	/**
+	 * Get the libircclient session.
+	 *
+	 * @warning Do not use this function, it is only required for ServerState's
+	 * @return the session
+	 */
+	inline irc_session_t *session() noexcept
+	{
+		return m_session.get();
+	}
+
+	/**
+	 * Change the channel mode.
+	 *
+	 * @param channel the channel
+	 * @param mode the new mode
+	 * @note Thread-safe
+	 */
+	inline void cmode(std::string channel, std::string mode)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_channel_mode(m_session.get(), channel.c_str(), mode.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Send a channel notice.
+	 *
+	 * @param channel the channel
+	 * @param message message notice
+	 * @note Thread-safe
+	 */
+	inline void cnotice(std::string channel, std::string message) noexcept
+	{
+		m_queue.push([=] () {
+			return irc_cmd_notice(this->m_session.get(), channel.c_str(), message.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Invite a user to a channel.
+	 *
+	 * @param target the target nickname
+	 * @param channel the channel
+	 * @note Thread-safe
+	 */
+	inline void invite(std::string target, std::string channel) noexcept
+	{
+		m_queue.push([=] () {
+			return irc_cmd_invite(this->m_session.get(), target.c_str(), channel.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Join a channel, the password is optional and can be kept empty.
+	 *
+	 * @param channel the channel to join
+	 * @param password the optional password
+	 * @note Thread-safe
+	 */
+	inline void join(std::string channel, std::string password = "") noexcept
+	{
+		m_queue.push([=] () {
+			const char *ptr = password.empty() ? nullptr : password.c_str();
+
+			return irc_cmd_join(this->m_session.get(), channel.c_str(), ptr) == 0;
+		});
+	}
+
+	/**
+	 * Kick someone from the channel. Please be sure to have the rights
+	 * on that channel because errors won't be reported.
+	 *
+	 * @param target the target to kick
+	 * @param channel from which channel
+	 * @param reason the optional reason
+	 * @note Thread-safe
+	 */
+	inline void kick(std::string target, std::string channel, std::string reason = "") noexcept
+	{
+		m_queue.push([=] () {
+			return irc_cmd_kick(this->m_session.get(), target.c_str(), channel.c_str(), reason.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Send a CTCP Action as known as /me. The target may be either a
+	 * channel or a nickname.
+	 *
+	 * @param target the nickname or the channel
+	 * @param message the message
+	 * @note Thread-safe
+	 */
+	inline void me(std::string target, std::string message)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_me(m_session.get(), target.c_str(), message.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Send a message to the specified target or channel.
+	 *
+	 * @param target the target
+	 * @param message the message
+	 * @note Thread-safe
+	 */
+	inline void message(std::string target, std::string message)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_msg(m_session.get(), target.c_str(), message.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Change your user mode.
+	 *
+	 * @param mode the mode
+	 * @note Thread-safe
+	 */
+	inline void mode(std::string mode)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_user_mode(m_session.get(), mode.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Request the list of names.
+	 *
+	 * @param channel the channel
+	 * @note Thread-safe
+	 */
+	inline void names(std::string channel)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_names(m_session.get(), channel.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Change your nickname.
+	 *
+	 * @param newnick the new nickname to use
+	 * @note Thread-safe
+	 */
+	inline void nick(std::string newnick)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_nick(m_session.get(), newnick.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Send a private notice.
+	 *
+	 * @param target the target
+	 * @param message the notice message
+	 * @note Thread-safe
+	 */
+	inline void notice(std::string target, std::string message)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_notice(m_session.get(), target.c_str(), message.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Part from a channel.
+	 *
+	 * Please note that the reason is not supported on all servers so if you want portability, don't provide it.
+	 *
+	 * @param channel the channel to leave
+	 * @param reason the optional reason
+	 * @note Thread-safe
+	 */
+	inline void part(std::string channel, std::string reason = "")
+	{
+		m_queue.push([=] () -> bool {
+			if (reason.empty())
+				return irc_cmd_part(m_session.get(), channel.c_str()) == 0;
+
+			return irc_send_raw(m_session.get(), "PART %s :%s", channel.c_str(), reason.c_str());
+		});
+	}
+
+	/**
+	 * Send a raw message to the IRC server. You don't need to add
+	 * message terminators.
+	 *
+	 * @warning Use this function with care
+	 * @param raw the raw message (without \r\n\r\n)
+	 * @note Thread-safe
+	 */
+	inline void send(std::string raw)
+	{
+		m_queue.push([=] () {
+			return irc_send_raw(m_session.get(), raw.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Change the channel topic.
+	 *
+	 * @param channel the channel
+	 * @param topic the desired topic
+	 * @note Thread-safe
+	 */
+	inline void topic(std::string channel, std::string topic)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_topic(m_session.get(), channel.c_str(), topic.c_str()) == 0;
+		});
+	}
+
+	/**
+	 * Request for whois information.
+	 *
+	 * @param target the target nickname
+	 * @note Thread-safe
+	 */
+	inline void whois(std::string target)
+	{
+		m_queue.push([=] () {
+			return irc_cmd_whois(this->m_session.get(), target.c_str()) == 0;
+		});
+	}
+
+#if defined(WITH_JS)
+	/**
+	 * Get the object signature.
+	 *
+	 * @return the signature
+	 */
+	static inline const char *name() noexcept
+	{
+		return "\xff""\xff""Server";
+	}
+
+	/**
+	 * Push the JavaScript prototype for a Server.
+	 *
+	 * @param ctx the context
+	 */
+	void prototype(js::Context &ctx);
+#endif
+};
+
+} // !irccd
+
+#endif // !_IRCCD_SERVER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/system.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,284 @@
+/*
+ * system.cpp -- platform dependent functions for system inspection
+ *
+ * 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 <cstdlib>
+#include <ctime>
+#include <stdexcept>
+
+#include <irccd-config.h>
+
+#if defined(HAVE_SETPROGNAME)
+#  include <cstdlib>
+#endif
+
+#if defined(IRCCD_SYSTEM_WINDOWS)
+#  include <sys/types.h>
+#  include <sys/timeb.h>
+#  include <Windows.h>
+#  include <Shlobj.h>
+#else // All non Windows
+#if defined(IRCCD_SYSTEM_MAC)
+#  include <sys/sysctl.h>
+#endif
+
+#if defined(IRCCD_SYSTEM_LINUX)
+#  include <sys/sysinfo.h>
+#endif
+
+#  include <sys/utsname.h>
+#  include <sys/time.h>
+#  include <sys/types.h>
+
+#  include <unistd.h>
+
+#  include <cerrno>
+#  include <cstring>
+#  include <stdexcept>
+#  include <ctime>
+
+#endif
+
+/* For sys::setGid */
+#if defined(HAVE_SETGID)
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <grp.h>
+#endif
+
+/* For sys::setUid */
+#if defined(HAVE_SETGID)
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <pwd.h>
+#endif
+
+#include "private/filesystem.h"
+
+#include "logger.h"
+#include "system.h"
+#include "util.h"
+
+namespace irccd {
+
+namespace sys {
+
+namespace {
+
+/*
+ * setHelper
+ * ------------------------------------------------------------------
+ *
+ * This is an helper for setting the uid or gid. It accepts both numeric and string uid and gid.
+ *
+ * If a name is specified as uid/group, the lookup function will be called and must be getpwname or
+ * getgrname. Then, to get the id from the returned structure (struct passwd, struct group), the getter
+ * function will return either pw_uid or gr_gid.
+ *
+ * Finally, when the id is resolved, the setter function (setuid, setgid) will be called.
+ *
+ * @param typeName the type of id (uid or gid)
+ * @param value the value (numeric or name)
+ * @param lookup the lookup function to resolve the name (getpwnam or getgrnam)
+ * @param setter the function to apply the id (setuid or setgid)
+ * @param getter the function to get the id from the informal structure
+ */
+template <typename IntType, typename LookupFunc, typename SetterFunc, typename FieldGetter>
+void setHelper(const std::string &typeName, const std::string &value, LookupFunc lookup, SetterFunc setter, FieldGetter getter)
+{
+	IntType id;
+
+	if (util::isNumber(value)) {
+		id = std::stoi(value);
+	} else {
+		auto info = lookup(value.c_str());
+
+		if (info == nullptr) {
+			log::warning() << "irccd: invalid " << typeName << ": " << std::strerror(errno) << std::endl;
+			return;
+		} else {
+			id = getter(info);
+
+			log::debug() << "irccd: " << typeName << " " << value << " resolved to: " << id << std::endl;
+		}
+	}
+
+	if (setter(id) < 0)
+		log::warning() << "irccd: could not set " << typeName << ": " << std::strerror(errno) << std::endl;
+	else
+		log::info() << "irccd: setting " << typeName << " to " << value << std::endl;
+}
+
+/*
+ * XXX: the setprogname() function keeps a pointer without copying it so when main's argv is modified, we're not using
+ * the same name so create our own copy.
+ */
+
+std::string programNameCopy;
+
+} // !namespace
+
+void setProgramName(std::string name) noexcept
+{
+	programNameCopy = std::move(name);
+
+#if defined(HAVE_SETPROGNAME)
+	setprogname(programNameCopy.c_str());
+#endif
+}
+
+const std::string &programName() noexcept
+{
+	return programNameCopy;
+}
+
+std::string name()
+{
+#if defined(IRCCD_SYSTEM_LINUX)
+	return "Linux";
+#elif defined(IRCCD_SYSTEM_WINDOWS)
+	return "Windows";
+#elif defined(IRCCD_SYSTEM_FREEBSD)
+	return "FreeBSD";
+#elif defined(IRCCD_SYSTEM_OPENBSD)
+	return "OpenBSD";
+#elif defined(IRCCD_SYSTEM_NETBSD)
+	return "NetBSD";
+#elif defined(IRCCD_SYSTEM_MAC)
+	return "Mac";
+#else
+	return "Unknown";
+#endif
+}
+
+std::string version()
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	auto version = GetVersion();
+	auto major = (DWORD)(LOBYTE(LOWORD(version)));
+	auto minor = (DWORD)(HIBYTE(LOWORD(version)));
+
+	return std::to_string(major) + "." + std::to_string(minor);
+#else
+	struct utsname uts;
+
+	if (uname(&uts) < 0)
+		throw std::runtime_error(std::strerror(errno));
+
+	return std::string(uts.release);
+#endif
+}
+
+uint64_t uptime()
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	return ::GetTickCount64() / 1000;
+#elif defined(IRCCD_SYSTEM_LINUX)
+	struct sysinfo info;
+
+	if (sysinfo(&info) < 0)
+		throw std::runtime_error(std::strerror(errno));
+
+	return info.uptime;
+#elif defined(IRCCD_SYSTEM_MAC)
+	struct timeval boottime;
+	size_t length = sizeof (boottime);
+	int mib[2] = { CTL_KERN, KERN_BOOTTIME };
+
+	if (sysctl(mib, 2, &boottime, &length, nullptr, 0) < 0)
+		throw std::runtime_error(std::strerror(errno));
+
+	time_t bsec = boottime.tv_sec, csec = time(nullptr);
+
+	return difftime(csec, bsec);
+#else
+	/* BSD */
+	struct timespec ts;
+
+	if (clock_gettime(CLOCK_UPTIME, &ts) < 0)
+		throw std::runtime_error(std::strerror(errno));
+
+	return ts.tv_sec;
+#endif
+}
+
+uint64_t ticks()
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	_timeb tp;
+
+	_ftime(&tp);
+
+	return tp.time * 1000LL + tp.millitm;
+#else
+	struct timeval tp;
+
+	gettimeofday(&tp, NULL);
+
+	return tp.tv_sec * 1000LL + tp.tv_usec / 1000;
+#endif
+}
+
+std::string home()
+{
+#if defined(IRCCD_SYSTEM_WINDOWS)
+	char path[MAX_PATH];
+
+	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK)
+		return "";
+
+	return std::string(path);
+#else
+	return env("HOME");
+#endif
+}
+
+std::string env(const std::string &var)
+{
+	auto value = std::getenv(var.c_str());
+
+	if (value == nullptr)
+		return "";
+
+	return value;
+}
+
+#if defined(HAVE_SETUID)
+
+void setUid(const std::string &value)
+{
+	setHelper<uid_t>("uid", value, &getpwnam, &setuid, [] (const struct passwd *pw) {
+		return pw->pw_uid;
+	});
+}
+
+#endif
+
+#if defined(HAVE_SETGID)
+
+void setGid(const std::string &value)
+{
+	setHelper<gid_t>("gid", value, &getgrnam, &setgid, [] (const struct group *gr) {
+		return gr->gr_gid;
+	});
+}
+
+#endif
+
+} // !sys
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/system.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,119 @@
+/*
+ * system.h -- platform dependent functions for system inspection
+ *
+ * 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_SYSTEM_H_
+#define _IRCCD_SYSTEM_H_
+
+/**
+ * @file system.h
+ * @brief System dependant functions
+ */
+
+#include <irccd-config.h>
+
+#include <cstdint>
+#include <string>
+
+namespace irccd {
+
+namespace sys {
+
+/**
+ * Set the program name, needed for some functions or some systems.
+ *
+ * @param name the program name
+ */
+void setProgramName(std::string name) noexcept;
+
+/**
+ * Get the program name.
+ *
+ * @return the program name
+ */
+const std::string &programName() noexcept;
+
+/**
+ * Get the system name.
+ *
+ * @return the name
+ */
+std::string name();
+
+/**
+ * Get the system version.
+ *
+ * @return the version
+ */
+std::string version();
+
+/**
+ * Get the number of seconds elapsed since the boottime.
+ *
+ * @return the number of seconds
+ */
+uint64_t uptime();
+
+/**
+ * Get the milliseconds elapsed since the application
+ * startup.
+ *
+ * @return the milliseconds
+ */
+uint64_t ticks();
+
+/**
+ * Get an environment variable.
+ *
+ * @return the value or empty string
+ */
+std::string env(const std::string &var);
+
+/**
+ * Get home directory usually /home/foo
+ *
+ * @return the home directory
+ */
+std::string home();
+
+#if defined(HAVE_SETUID)
+
+/**
+ * Set the effective uid by name or numeric value.
+ *
+ * @param value the value
+ */
+void setUid(const std::string &value);
+
+#endif
+
+#if defined(HAVE_SETGID)
+
+/**
+ * Set the effective gid by name or numeric value.
+ *
+ * @param value the value
+ */
+void setGid(const std::string &value);
+
+#endif
+
+} // !sys
+
+} // !irccd
+
+#endif // !_IRCCD_SYSTEM_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/timer.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,86 @@
+/*
+ * timer.cpp -- threaded timers
+ *
+ * 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 <chrono>
+
+#include "timer.h"
+
+namespace irccd {
+
+void Timer::run()
+{
+	while (m_state != Stopped) {
+		std::unique_lock<std::mutex> lock(m_mutex);
+
+		/* Wait in case the timer is paused */
+		m_condition.wait(lock, [&] () {
+			return m_state != Paused;
+		});
+
+		if (m_state != Running)
+			continue;
+
+		/* Wait the timer delay or the interrupt */
+		m_condition.wait_for(lock, std::chrono::milliseconds(m_delay), [&] () {
+			return m_state != Running;
+		});
+
+		if (m_state == Running) {
+			/* Signal process */
+			onSignal();
+
+			if (m_type == TimerType::Single)
+				m_state = Stopped;
+		}
+	}
+
+	onEnd();
+}
+
+Timer::Timer(TimerType type, unsigned delay) noexcept
+	: m_type(type)
+	, m_delay(delay)
+	, m_thread([this] () { run(); })
+{
+}
+
+Timer::~Timer()
+{
+	assert(m_state != Running);
+
+	m_state = Stopped;
+	m_condition.notify_one();
+	m_thread.join();
+}
+
+void Timer::start()
+{
+	assert(m_state != Running);
+
+	m_state = Running;
+	m_condition.notify_one();
+}
+
+void Timer::stop()
+{
+	m_state = Paused;
+	m_condition.notify_one();
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/timer.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,156 @@
+/*
+ * timer.h -- threaded timers
+ *
+ * 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_TIMER_H_
+#define _IRCCD_TIMER_H_
+
+/**
+ * @file Timer.h
+ * @brief Provides interval based timers for JavaScript
+ */
+
+#include <atomic>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <irccd/private/signals.h>
+
+namespace irccd {
+
+/**
+ * @enum TimerType
+ * @brief Type of timer
+ */
+enum class TimerType {
+	Single,			//!< The timer ends after execution
+	Repeat			//!< The timer loops
+};
+
+/**
+ * @class Timer
+ * @brief Timer class
+ *
+ * A timer is a thread object that emits a signal periodically or just one time. It is perfectly pausable and resumable
+ * to reuse the same object.
+ *
+ * The delay is configured in milliseconds and the user has choice to use any
+ * delay needed.
+ *
+ * We use a condition variable to wait for the specified delay unless the timer
+ * must be stopped.
+ */
+class Timer {
+public:
+	/**
+	 * Signal: onSignal
+	 * ----------------------------------------------------------
+	 *
+	 * Called when the timeout expires.
+	 */
+	Signal<> onSignal;
+
+	/**
+	 * Signal: onEnd
+	 * ----------------------------------------------------------
+	 *
+	 * Called when the timeout ends.
+	 */
+	Signal<> onEnd;
+
+private:
+	enum {
+		Paused,
+		Running,
+		Stopped
+	};
+
+	TimerType m_type;
+	unsigned m_delay;
+
+	/* Thread management */
+	std::atomic<int> m_state{Paused};
+	std::mutex m_mutex;
+	std::condition_variable m_condition;
+	std::thread m_thread;
+
+	void run();
+
+public:
+	/**
+	 * Timer constructor.
+	 *
+	 * The timer is not started, use start().
+	 *
+	 * @param type the timer type
+	 * @param delay the delay in milliseconds
+	 * @post isRunning() returns false
+	 */
+	Timer(TimerType type, unsigned delay) noexcept;
+
+	/**
+	 * Destructor, closes the thread.
+	 *
+	 * @pre stop() must have been called.
+	 */
+	virtual ~Timer();
+
+	/**
+	 * Start the thread.
+	 *
+	 * @pre isRunning() must return false
+	 * @pre onSignal() must have been called
+	 * @pre onEnd() must have been called
+	 * @note Thread-safe
+	 */
+	void start();
+
+	/**
+	 * Stop the timer, may be used by the user to stop it.
+	 *
+	 * @pre isRunning() must return true
+	 * @note Thread-safe
+	 */
+	void stop();
+
+	/**
+	 * Get the type of timer.
+	 *
+	 * @return the type.
+	 */
+	inline TimerType type() const noexcept
+	{
+		return m_type;
+	}
+
+	/**
+	 * Tells if the timer has still a running thread.
+	 *
+	 * @return true if still alive
+	 * @note Thread-safe
+	 */
+	inline bool isRunning() const noexcept
+	{
+		return m_state == Running;
+	}
+};
+
+} // !irccd
+
+#endif // !_IRCCD_TIMER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/transport-client.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,74 @@
+/*
+ * transport-client.cpp -- client connected 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 "json.h"
+#include "logger.h"
+#include "transport-client.h"
+
+namespace irccd {
+
+void TransportClient::parse(const std::string &message)
+{
+	json::Value document(json::Buffer{message});
+
+	if (!document.isObject())
+		throw std::invalid_argument("the message is not a valid JSON object");
+
+	onCommand(document);
+}
+
+void TransportClient::sync(fd_set &setinput, fd_set &setoutput)
+{
+	if (FD_ISSET(handle(), &setinput)) {
+		log::debug() << "transport: receiving to input buffer" << std::endl;
+		receive();
+	}
+	if (FD_ISSET(handle(), &setoutput)) {
+		log::debug() << "transport: sending outgoing buffer" << std::endl;
+		send();
+	}
+}
+
+#if 0
+
+void TransportClient::ok(const std::string &command)
+{
+	m_output += "{";
+	m_output += "\"response\":\"" + command + "\",";
+	m_output += "\"status\":\"ok\"";
+	m_output += "}\r\n\r\n";
+}
+
+void TransportClient::error(const std::string &command, std::string message)
+{
+	m_output += "{";
+	m_output += "\"response\":\"" + command + "\",";
+	m_output += "\"status\":\"error\",";
+	m_output += "\"error\": \"" + json::escape(message) + "\"";
+	m_output += "}\r\n\r\n";
+}
+
+#endif
+
+void TransportClient::send(std::string message)
+{
+	m_output += message;
+	m_output += "\r\n\r\n";
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/transport-client.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,209 @@
+/*
+ * transport-client.h -- client connected 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_TRANSPORT_CLIENT_H_
+#define _IRCCD_TRANSPORT_CLIENT_H_
+
+/**
+ * @file TransportClient.h
+ * @brief Client connected to irccd
+ */
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <irccd/private/signals.h>
+#include <irccd/private/sockets.h>
+
+#include "server.h"
+
+namespace irccd {
+
+namespace json {
+
+class Value;
+
+} // !json
+
+/**
+ * @class TransportClient
+ * @brief Client connected to irccd.
+ *
+ * This class emits a warning upon clients request through onCommand signal.
+ */
+class TransportClient {
+public:
+	/**
+	 * Signal: onCommand
+	 * ----------------------------------------------------------
+	 *
+	 * Arguments:
+	 *   - the command
+	 */
+	Signal<const json::Value &> onCommand;
+
+	/**
+	 * Signal: onDie
+	 * ----------------------------------------------------------
+	 *
+	 * The client has disconnected.
+	 */
+	Signal<> onDie;
+
+protected:
+	std::string m_input;
+	std::string m_output;
+
+	/* Parse input buffer */
+	void parse(const std::string &);
+
+	/* Do I/O */
+	virtual void receive() = 0;
+	virtual void send() = 0;
+
+public:
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~TransportClient() = default;
+
+	/**
+	 * Send or receive data, called after a select.
+	 *
+	 * @param setinput the input fd_set
+	 * @param setoutput the output fd_set
+	 */
+	void sync(fd_set &setinput, fd_set &setoutput);
+
+#if 0
+	/**
+	 * Notify the client that the command succeeded.
+	 *
+	 * @param command the command name
+	 */
+	void ok(const std::string &command);
+
+	/**
+	 * Send an error message to the client.
+	 *
+	 * @param command the command name
+	 * @param message the error message
+	 */
+	void error(const std::string &command, std::string message);
+#endif
+
+	/**
+	 * Send some data, it will be pushed to the outgoing buffer.
+	 *
+	 * This function appends "\r\n\r\n" after the message so you don't have
+	 * to do it manually.
+	 *
+	 * @param message the message
+	 */
+	void send(std::string message);
+
+	/**
+	 * Tell if the client has data pending for output.
+	 *
+	 * @return true if has pending data to write
+	 */
+	inline bool hasOutput() const noexcept
+	{
+		return !m_output.empty();
+	}
+
+	/**
+	 * Get the underlying socket handle.
+	 *
+	 * @return the socket
+	 */
+	virtual net::Handle handle() noexcept = 0;
+};
+
+/**
+ * @class TransportClient
+ * @brief Template class for Tcp and Ssl sockets
+ */
+template <typename Address>
+class TransportClientBase : public TransportClient {
+private:
+	net::SocketTcp<Address> m_socket;
+
+protected:
+	void send() override;
+	void receive() override;
+
+public:
+	/**
+	 * Create a client.
+	 *
+	 * @param sock the socket
+	 */
+	inline TransportClientBase(net::SocketTcp<Address> socket)
+		: m_socket(std::move(socket))
+	{
+	}
+
+	/**
+	 * @copydoc TransportClient::handle
+	 */
+	net::Handle handle() noexcept override
+	{
+		return m_socket.handle();
+	}
+};
+
+template <typename Address>
+void TransportClientBase<Address>::receive()
+{
+	try {
+		auto message = m_socket.recv(512);
+
+		if (message.empty())
+			onDie();
+
+		m_input += message;
+	} catch (const std::exception &) {
+		onDie();
+	}
+
+	std::string::size_type pos;
+	while ((pos = m_input.find("\r\n\r\n")) != std::string::npos) {
+		/*
+		 * Make a copy and erase it in case that onComplete function
+		 * throws.
+		 */
+		auto message = m_input.substr(0, pos);
+
+		m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
+
+		parse(message);
+	}
+}
+
+template <typename Address>
+void TransportClientBase<Address>::send()
+{
+	m_output.erase(0, m_socket.send(m_output));
+}
+
+} // !irccd
+
+#endif // !_IRCCD_TRANSPORT_CLIENT_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/transport-server.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,92 @@
+/*
+ * transport-server.cpp -- I/O for irccd clients (acceptors)
+ *
+ * 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.
+ */
+
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+#  include <cstdio>
+#endif
+
+#include <sstream>
+
+#include "transport-server.h"
+
+namespace irccd {
+
+/*
+ * TransportServerIp
+ * ------------------------------------------------------------------
+ */
+
+TransportServerIp::TransportServerIp(int domain, const std::string &address, int port, bool ipv6only)
+	: m_socket(domain, SOCK_STREAM, 0)
+{
+	m_socket.set(net::option::SockReuseAddress{true});
+
+	/* Disable or enable IPv4 when using IPv6 */
+	if (domain == AF_INET6)
+		m_socket.set(net::option::Ipv6Only{ipv6only});
+
+	m_socket.bind(net::address::Ip{address, port, static_cast<net::address::Ip::Type>(domain)});
+	m_socket.listen();
+
+	log::info() << "transport: listening on " << address << ", port " << port << std::endl;
+}
+
+net::Handle TransportServerIp::handle() noexcept
+{
+	return m_socket.handle();
+}
+
+std::shared_ptr<TransportClient> TransportServerIp::accept()
+{
+	return std::make_shared<TransportClientBase<net::address::Ip>>(m_socket.accept(nullptr));
+}
+
+/*
+ * TransportServerUnix
+ * ------------------------------------------------------------------
+ */
+
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+
+TransportServerUnix::TransportServerUnix(std::string path)
+	: m_path(std::move(path))
+{
+	m_socket.bind(net::address::Local{m_path, true});
+	m_socket.listen();
+
+	log::info() << "transport: listening on " << m_path << std::endl;
+}
+
+TransportServerUnix::~TransportServerUnix()
+{
+	::remove(m_path.c_str());
+}
+
+net::Handle TransportServerUnix::handle() noexcept
+{
+	return m_socket.handle();
+}
+
+std::shared_ptr<TransportClient> TransportServerUnix::accept()
+{
+	return std::make_shared<TransportClientBase<net::address::Local>>(m_socket.accept(nullptr));
+}
+
+#endif
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/transport-server.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,156 @@
+/*
+ * transport-server.h -- I/O for irccd clients (acceptors)
+ *
+ * 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_TRANSPORT_SERVER_H_
+#define _IRCCD_TRANSPORT_SERVER_H_
+
+/**
+ * @file transport-server.h
+ * @brief Transports for irccd
+ */
+
+#include <memory>
+#include <string>
+
+#include <irccd-config.h>
+#include <irccd/private/sockets.h>
+
+#include "transport-client.h"
+
+namespace irccd {
+
+/**
+ * @class TransportServer
+ * @brief Bring networking between irccd and irccdctl
+ *
+ * This class contains a master sockets for listening to TCP connections, it is then processed by irccd.
+ *
+ * The transport class supports the following domains:
+ *
+ * | Domain                | Class                 |
+ * |-----------------------|-----------------------|
+ * | IPv4, IPv6            | TransportServerIp     |
+ * | Unix (not on Windows) | TransportServerUnix   |
+ *
+ * Note: IPv4 and IPv6 can be combined, using TransportServer::IPv6 and its option.
+ */
+class TransportServer {
+private:
+	TransportServer(const TransportServer &) = delete;
+	TransportServer(TransportServer &&) = delete;
+
+	TransportServer &operator=(const TransportServer &) = delete;
+	TransportServer &operator=(TransportServer &&) = delete;
+
+public:
+	/**
+	 * Default constructor.
+	 */
+	TransportServer() = default;
+
+	/**
+	 * Destructor defaulted.
+	 */
+	virtual ~TransportServer() = default;
+
+	/**
+	 * Retrieve the underlying socket handle.
+	 *
+	 * @return the socket
+	 */
+	virtual net::Handle handle() noexcept = 0;
+
+	/**
+	 * Accept a new client depending on the domain.
+	 *
+	 * @return the new client
+	 */
+	virtual std::shared_ptr<TransportClient> accept() = 0;
+};
+
+/**
+ * @class TransportServerIp
+ * @brief Base class for both IPv4 and IPv6 servers.
+ */
+class TransportServerIp : public TransportServer {
+protected:
+	net::SocketTcp<net::address::Ip> m_socket;
+
+public:
+	/**
+	 * Create a IP transport, use IPv6 or IPv4 address.
+	 *
+	 * @param domain AF_INET or AF_INET6
+	 * @param address the address or "*" for any
+	 * @param port the port number
+	 * @param ipv6only set to true to disable IPv4
+	 * @throw net::Error on failures
+	 */
+	TransportServerIp(int domain, const std::string &address, int port, bool ipv6only = true);
+
+	/**
+	 * @copydoc TransportServer::socket
+	 */
+	net::Handle handle() noexcept override;
+
+	/**
+	 * @copydoc TransportServer::accept
+	 */
+	std::shared_ptr<TransportClient> accept() override;
+};
+
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+
+/**
+ * @class TransportServerUnix
+ * @brief Implementation of transports for Unix sockets.
+ */
+class TransportServerUnix : public TransportServer {
+private:
+	net::SocketTcp<net::address::Local> m_socket;
+	std::string m_path;
+
+public:
+	/**
+	 * Create a Unix transport.
+	 *
+	 * @param path the path
+	 */
+	TransportServerUnix(std::string path);
+
+	/**
+	 * Destroy the transport and remove the file.
+	 */
+	~TransportServerUnix();
+
+	/**
+	 * @copydoc TransportServer::socket
+	 */
+	net::Handle handle() noexcept override;
+
+	/**
+	 * @copydoc TransportServer::accept
+	 */
+	std::shared_ptr<TransportClient> accept() override;
+};
+
+#endif // !_WIN32
+
+} // !irccd
+
+#endif // !_IRCCD_TRANSPORT_SERVER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/unicode.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,4712 @@
+/*
+ * unicode.cpp -- UTF-8 to UTF-32 conversions and various operations
+ *
+ * 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 "unicode.h"
+
+/*
+ * The following code has been generated from Go mkrunetype adapted to our
+ * needs.
+ */
+
+namespace irccd {
+
+namespace unicode {
+
+#define nelem(x) (sizeof (x) / sizeof ((x)[0]))
+
+char32_t *rbsearch(char32_t c, char32_t *t, int n, int ne) noexcept
+{
+	char32_t *p;
+	int m;
+
+	while (n > 1) {
+		m = n >> 1;
+		p = t + m * ne;
+
+		if (c >= p[0]) {
+			t = p;
+			n = n - m;
+		} else {
+			n = m;
+		}
+	}
+
+	if (n && c >= t[0])
+		return t;
+
+	return nullptr;
+}
+
+static char32_t isspacer[] = {
+	0x0009, 0x000d,
+	0x0020, 0x0020,
+	0x0085, 0x0085,
+	0x00a0, 0x00a0,
+	0x1680, 0x1680,
+	0x2000, 0x200a,
+	0x2028, 0x2029,
+	0x202f, 0x202f,
+	0x205f, 0x205f,
+	0x3000, 0x3000,
+	0xfeff, 0xfeff,
+};
+
+bool isspace(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, isspacer, nelem (isspacer)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	return false;
+}
+
+static char32_t isdigitr[] = {
+	0x0030, 0x0039,
+	0x0660, 0x0669,
+	0x06f0, 0x06f9,
+	0x07c0, 0x07c9,
+	0x0966, 0x096f,
+	0x09e6, 0x09ef,
+	0x0a66, 0x0a6f,
+	0x0ae6, 0x0aef,
+	0x0b66, 0x0b6f,
+	0x0be6, 0x0bef,
+	0x0c66, 0x0c6f,
+	0x0ce6, 0x0cef,
+	0x0d66, 0x0d6f,
+	0x0de6, 0x0def,
+	0x0e50, 0x0e59,
+	0x0ed0, 0x0ed9,
+	0x0f20, 0x0f29,
+	0x1040, 0x1049,
+	0x1090, 0x1099,
+	0x17e0, 0x17e9,
+	0x1810, 0x1819,
+	0x1946, 0x194f,
+	0x19d0, 0x19d9,
+	0x1a80, 0x1a89,
+	0x1a90, 0x1a99,
+	0x1b50, 0x1b59,
+	0x1bb0, 0x1bb9,
+	0x1c40, 0x1c49,
+	0x1c50, 0x1c59,
+	0xa620, 0xa629,
+	0xa8d0, 0xa8d9,
+	0xa900, 0xa909,
+	0xa9d0, 0xa9d9,
+	0xa9f0, 0xa9f9,
+	0xaa50, 0xaa59,
+	0xabf0, 0xabf9,
+	0xff10, 0xff19,
+	0x104a0, 0x104a9,
+	0x11066, 0x1106f,
+	0x110f0, 0x110f9,
+	0x11136, 0x1113f,
+	0x111d0, 0x111d9,
+	0x112f0, 0x112f9,
+	0x114d0, 0x114d9,
+	0x11650, 0x11659,
+	0x116c0, 0x116c9,
+	0x118e0, 0x118e9,
+	0x16a60, 0x16a69,
+	0x16b50, 0x16b59,
+	0x1d7ce, 0x1d7ff,
+};
+
+bool isdigit(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, isdigitr, nelem (isdigitr)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	return false;
+}
+
+static char32_t isalphar[] = {
+	0x0041, 0x005a,
+	0x0061, 0x007a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00f6,
+	0x00f8, 0x02c1,
+	0x02c6, 0x02d1,
+	0x02e0, 0x02e4,
+	0x0370, 0x0374,
+	0x0376, 0x0377,
+	0x037a, 0x037d,
+	0x0388, 0x038a,
+	0x038e, 0x03a1,
+	0x03a3, 0x03f5,
+	0x03f7, 0x0481,
+	0x048a, 0x052f,
+	0x0531, 0x0556,
+	0x0561, 0x0587,
+	0x05d0, 0x05ea,
+	0x05f0, 0x05f2,
+	0x0620, 0x064a,
+	0x066e, 0x066f,
+	0x0671, 0x06d3,
+	0x06e5, 0x06e6,
+	0x06ee, 0x06ef,
+	0x06fa, 0x06fc,
+	0x0712, 0x072f,
+	0x074d, 0x07a5,
+	0x07ca, 0x07ea,
+	0x07f4, 0x07f5,
+	0x0800, 0x0815,
+	0x0840, 0x0858,
+	0x08a0, 0x08b2,
+	0x0904, 0x0939,
+	0x0958, 0x0961,
+	0x0971, 0x0980,
+	0x0985, 0x098c,
+	0x098f, 0x0990,
+	0x0993, 0x09a8,
+	0x09aa, 0x09b0,
+	0x09b6, 0x09b9,
+	0x09dc, 0x09dd,
+	0x09df, 0x09e1,
+	0x09f0, 0x09f1,
+	0x0a05, 0x0a0a,
+	0x0a0f, 0x0a10,
+	0x0a13, 0x0a28,
+	0x0a2a, 0x0a30,
+	0x0a32, 0x0a33,
+	0x0a35, 0x0a36,
+	0x0a38, 0x0a39,
+	0x0a59, 0x0a5c,
+	0x0a72, 0x0a74,
+	0x0a85, 0x0a8d,
+	0x0a8f, 0x0a91,
+	0x0a93, 0x0aa8,
+	0x0aaa, 0x0ab0,
+	0x0ab2, 0x0ab3,
+	0x0ab5, 0x0ab9,
+	0x0ae0, 0x0ae1,
+	0x0b05, 0x0b0c,
+	0x0b0f, 0x0b10,
+	0x0b13, 0x0b28,
+	0x0b2a, 0x0b30,
+	0x0b32, 0x0b33,
+	0x0b35, 0x0b39,
+	0x0b5c, 0x0b5d,
+	0x0b5f, 0x0b61,
+	0x0b85, 0x0b8a,
+	0x0b8e, 0x0b90,
+	0x0b92, 0x0b95,
+	0x0b99, 0x0b9a,
+	0x0b9e, 0x0b9f,
+	0x0ba3, 0x0ba4,
+	0x0ba8, 0x0baa,
+	0x0bae, 0x0bb9,
+	0x0c05, 0x0c0c,
+	0x0c0e, 0x0c10,
+	0x0c12, 0x0c28,
+	0x0c2a, 0x0c39,
+	0x0c58, 0x0c59,
+	0x0c60, 0x0c61,
+	0x0c85, 0x0c8c,
+	0x0c8e, 0x0c90,
+	0x0c92, 0x0ca8,
+	0x0caa, 0x0cb3,
+	0x0cb5, 0x0cb9,
+	0x0ce0, 0x0ce1,
+	0x0cf1, 0x0cf2,
+	0x0d05, 0x0d0c,
+	0x0d0e, 0x0d10,
+	0x0d12, 0x0d3a,
+	0x0d60, 0x0d61,
+	0x0d7a, 0x0d7f,
+	0x0d85, 0x0d96,
+	0x0d9a, 0x0db1,
+	0x0db3, 0x0dbb,
+	0x0dc0, 0x0dc6,
+	0x0e01, 0x0e30,
+	0x0e32, 0x0e33,
+	0x0e40, 0x0e46,
+	0x0e81, 0x0e82,
+	0x0e87, 0x0e88,
+	0x0e94, 0x0e97,
+	0x0e99, 0x0e9f,
+	0x0ea1, 0x0ea3,
+	0x0eaa, 0x0eab,
+	0x0ead, 0x0eb0,
+	0x0eb2, 0x0eb3,
+	0x0ec0, 0x0ec4,
+	0x0edc, 0x0edf,
+	0x0f40, 0x0f47,
+	0x0f49, 0x0f6c,
+	0x0f88, 0x0f8c,
+	0x1000, 0x102a,
+	0x1050, 0x1055,
+	0x105a, 0x105d,
+	0x1065, 0x1066,
+	0x106e, 0x1070,
+	0x1075, 0x1081,
+	0x10a0, 0x10c5,
+	0x10d0, 0x10fa,
+	0x10fc, 0x1248,
+	0x124a, 0x124d,
+	0x1250, 0x1256,
+	0x125a, 0x125d,
+	0x1260, 0x1288,
+	0x128a, 0x128d,
+	0x1290, 0x12b0,
+	0x12b2, 0x12b5,
+	0x12b8, 0x12be,
+	0x12c2, 0x12c5,
+	0x12c8, 0x12d6,
+	0x12d8, 0x1310,
+	0x1312, 0x1315,
+	0x1318, 0x135a,
+	0x1380, 0x138f,
+	0x13a0, 0x13f4,
+	0x1401, 0x166c,
+	0x166f, 0x167f,
+	0x1681, 0x169a,
+	0x16a0, 0x16ea,
+	0x16f1, 0x16f8,
+	0x1700, 0x170c,
+	0x170e, 0x1711,
+	0x1720, 0x1731,
+	0x1740, 0x1751,
+	0x1760, 0x176c,
+	0x176e, 0x1770,
+	0x1780, 0x17b3,
+	0x1820, 0x1877,
+	0x1880, 0x18a8,
+	0x18b0, 0x18f5,
+	0x1900, 0x191e,
+	0x1950, 0x196d,
+	0x1970, 0x1974,
+	0x1980, 0x19ab,
+	0x19c1, 0x19c7,
+	0x1a00, 0x1a16,
+	0x1a20, 0x1a54,
+	0x1b05, 0x1b33,
+	0x1b45, 0x1b4b,
+	0x1b83, 0x1ba0,
+	0x1bae, 0x1baf,
+	0x1bba, 0x1be5,
+	0x1c00, 0x1c23,
+	0x1c4d, 0x1c4f,
+	0x1c5a, 0x1c7d,
+	0x1ce9, 0x1cec,
+	0x1cee, 0x1cf1,
+	0x1cf5, 0x1cf6,
+	0x1d00, 0x1dbf,
+	0x1e00, 0x1f15,
+	0x1f18, 0x1f1d,
+	0x1f20, 0x1f45,
+	0x1f48, 0x1f4d,
+	0x1f50, 0x1f57,
+	0x1f5f, 0x1f7d,
+	0x1f80, 0x1fb4,
+	0x1fb6, 0x1fbc,
+	0x1fc2, 0x1fc4,
+	0x1fc6, 0x1fcc,
+	0x1fd0, 0x1fd3,
+	0x1fd6, 0x1fdb,
+	0x1fe0, 0x1fec,
+	0x1ff2, 0x1ff4,
+	0x1ff6, 0x1ffc,
+	0x2090, 0x209c,
+	0x210a, 0x2113,
+	0x2119, 0x211d,
+	0x212a, 0x212d,
+	0x212f, 0x2139,
+	0x213c, 0x213f,
+	0x2145, 0x2149,
+	0x2183, 0x2184,
+	0x2c00, 0x2c2e,
+	0x2c30, 0x2c5e,
+	0x2c60, 0x2ce4,
+	0x2ceb, 0x2cee,
+	0x2cf2, 0x2cf3,
+	0x2d00, 0x2d25,
+	0x2d30, 0x2d67,
+	0x2d80, 0x2d96,
+	0x2da0, 0x2da6,
+	0x2da8, 0x2dae,
+	0x2db0, 0x2db6,
+	0x2db8, 0x2dbe,
+	0x2dc0, 0x2dc6,
+	0x2dc8, 0x2dce,
+	0x2dd0, 0x2dd6,
+	0x2dd8, 0x2dde,
+	0x3005, 0x3006,
+	0x3031, 0x3035,
+	0x303b, 0x303c,
+	0x3041, 0x3096,
+	0x309d, 0x309f,
+	0x30a1, 0x30fa,
+	0x30fc, 0x30ff,
+	0x3105, 0x312d,
+	0x3131, 0x318e,
+	0x31a0, 0x31ba,
+	0x31f0, 0x31ff,
+	0x3400, 0x4db5,
+	0x4e00, 0x9fcc,
+	0xa000, 0xa48c,
+	0xa4d0, 0xa4fd,
+	0xa500, 0xa60c,
+	0xa610, 0xa61f,
+	0xa62a, 0xa62b,
+	0xa640, 0xa66e,
+	0xa67f, 0xa69d,
+	0xa6a0, 0xa6e5,
+	0xa717, 0xa71f,
+	0xa722, 0xa788,
+	0xa78b, 0xa78e,
+	0xa790, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xa7f7, 0xa801,
+	0xa803, 0xa805,
+	0xa807, 0xa80a,
+	0xa80c, 0xa822,
+	0xa840, 0xa873,
+	0xa882, 0xa8b3,
+	0xa8f2, 0xa8f7,
+	0xa90a, 0xa925,
+	0xa930, 0xa946,
+	0xa960, 0xa97c,
+	0xa984, 0xa9b2,
+	0xa9e0, 0xa9e4,
+	0xa9e6, 0xa9ef,
+	0xa9fa, 0xa9fe,
+	0xaa00, 0xaa28,
+	0xaa40, 0xaa42,
+	0xaa44, 0xaa4b,
+	0xaa60, 0xaa76,
+	0xaa7e, 0xaaaf,
+	0xaab5, 0xaab6,
+	0xaab9, 0xaabd,
+	0xaadb, 0xaadd,
+	0xaae0, 0xaaea,
+	0xaaf2, 0xaaf4,
+	0xab01, 0xab06,
+	0xab09, 0xab0e,
+	0xab11, 0xab16,
+	0xab20, 0xab26,
+	0xab28, 0xab2e,
+	0xab30, 0xab5a,
+	0xab5c, 0xab5f,
+	0xab64, 0xab65,
+	0xabc0, 0xabe2,
+	0xac00, 0xd7a3,
+	0xd7b0, 0xd7c6,
+	0xd7cb, 0xd7fb,
+	0xf900, 0xfa6d,
+	0xfa70, 0xfad9,
+	0xfb00, 0xfb06,
+	0xfb13, 0xfb17,
+	0xfb1f, 0xfb28,
+	0xfb2a, 0xfb36,
+	0xfb38, 0xfb3c,
+	0xfb40, 0xfb41,
+	0xfb43, 0xfb44,
+	0xfb46, 0xfbb1,
+	0xfbd3, 0xfd3d,
+	0xfd50, 0xfd8f,
+	0xfd92, 0xfdc7,
+	0xfdf0, 0xfdfb,
+	0xfe70, 0xfe74,
+	0xfe76, 0xfefc,
+	0xff21, 0xff3a,
+	0xff41, 0xff5a,
+	0xff66, 0xffbe,
+	0xffc2, 0xffc7,
+	0xffca, 0xffcf,
+	0xffd2, 0xffd7,
+	0xffda, 0xffdc,
+	0x10000, 0x1000b,
+	0x1000d, 0x10026,
+	0x10028, 0x1003a,
+	0x1003c, 0x1003d,
+	0x1003f, 0x1004d,
+	0x10050, 0x1005d,
+	0x10080, 0x100fa,
+	0x10280, 0x1029c,
+	0x102a0, 0x102d0,
+	0x10300, 0x1031f,
+	0x10330, 0x10340,
+	0x10342, 0x10349,
+	0x10350, 0x10375,
+	0x10380, 0x1039d,
+	0x103a0, 0x103c3,
+	0x103c8, 0x103cf,
+	0x10400, 0x1049d,
+	0x10500, 0x10527,
+	0x10530, 0x10563,
+	0x10600, 0x10736,
+	0x10740, 0x10755,
+	0x10760, 0x10767,
+	0x10800, 0x10805,
+	0x1080a, 0x10835,
+	0x10837, 0x10838,
+	0x1083f, 0x10855,
+	0x10860, 0x10876,
+	0x10880, 0x1089e,
+	0x10900, 0x10915,
+	0x10920, 0x10939,
+	0x10980, 0x109b7,
+	0x109be, 0x109bf,
+	0x10a10, 0x10a13,
+	0x10a15, 0x10a17,
+	0x10a19, 0x10a33,
+	0x10a60, 0x10a7c,
+	0x10a80, 0x10a9c,
+	0x10ac0, 0x10ac7,
+	0x10ac9, 0x10ae4,
+	0x10b00, 0x10b35,
+	0x10b40, 0x10b55,
+	0x10b60, 0x10b72,
+	0x10b80, 0x10b91,
+	0x10c00, 0x10c48,
+	0x11003, 0x11037,
+	0x11083, 0x110af,
+	0x110d0, 0x110e8,
+	0x11103, 0x11126,
+	0x11150, 0x11172,
+	0x11183, 0x111b2,
+	0x111c1, 0x111c4,
+	0x11200, 0x11211,
+	0x11213, 0x1122b,
+	0x112b0, 0x112de,
+	0x11305, 0x1130c,
+	0x1130f, 0x11310,
+	0x11313, 0x11328,
+	0x1132a, 0x11330,
+	0x11332, 0x11333,
+	0x11335, 0x11339,
+	0x1135d, 0x11361,
+	0x11480, 0x114af,
+	0x114c4, 0x114c5,
+	0x11580, 0x115ae,
+	0x11600, 0x1162f,
+	0x11680, 0x116aa,
+	0x118a0, 0x118df,
+	0x11ac0, 0x11af8,
+	0x12000, 0x12398,
+	0x13000, 0x1342e,
+	0x16800, 0x16a38,
+	0x16a40, 0x16a5e,
+	0x16ad0, 0x16aed,
+	0x16b00, 0x16b2f,
+	0x16b40, 0x16b43,
+	0x16b63, 0x16b77,
+	0x16b7d, 0x16b8f,
+	0x16f00, 0x16f44,
+	0x16f93, 0x16f9f,
+	0x1b000, 0x1b001,
+	0x1bc00, 0x1bc6a,
+	0x1bc70, 0x1bc7c,
+	0x1bc80, 0x1bc88,
+	0x1bc90, 0x1bc99,
+	0x1d400, 0x1d454,
+	0x1d456, 0x1d49c,
+	0x1d49e, 0x1d49f,
+	0x1d4a5, 0x1d4a6,
+	0x1d4a9, 0x1d4ac,
+	0x1d4ae, 0x1d4b9,
+	0x1d4bd, 0x1d4c3,
+	0x1d4c5, 0x1d505,
+	0x1d507, 0x1d50a,
+	0x1d50d, 0x1d514,
+	0x1d516, 0x1d51c,
+	0x1d51e, 0x1d539,
+	0x1d53b, 0x1d53e,
+	0x1d540, 0x1d544,
+	0x1d54a, 0x1d550,
+	0x1d552, 0x1d6a5,
+	0x1d6a8, 0x1d6c0,
+	0x1d6c2, 0x1d6da,
+	0x1d6dc, 0x1d6fa,
+	0x1d6fc, 0x1d714,
+	0x1d716, 0x1d734,
+	0x1d736, 0x1d74e,
+	0x1d750, 0x1d76e,
+	0x1d770, 0x1d788,
+	0x1d78a, 0x1d7a8,
+	0x1d7aa, 0x1d7c2,
+	0x1d7c4, 0x1d7cb,
+	0x1e800, 0x1e8c4,
+	0x1ee00, 0x1ee03,
+	0x1ee05, 0x1ee1f,
+	0x1ee21, 0x1ee22,
+	0x1ee29, 0x1ee32,
+	0x1ee34, 0x1ee37,
+	0x1ee4d, 0x1ee4f,
+	0x1ee51, 0x1ee52,
+	0x1ee61, 0x1ee62,
+	0x1ee67, 0x1ee6a,
+	0x1ee6c, 0x1ee72,
+	0x1ee74, 0x1ee77,
+	0x1ee79, 0x1ee7c,
+	0x1ee80, 0x1ee89,
+	0x1ee8b, 0x1ee9b,
+	0x1eea1, 0x1eea3,
+	0x1eea5, 0x1eea9,
+	0x1eeab, 0x1eebb,
+	0x20000, 0x2a6d6,
+	0x2a700, 0x2b734,
+	0x2b740, 0x2b81d,
+	0x2f800, 0x2fa1d,
+};
+
+static char32_t isalphas[] = {
+	0x00aa,
+	0x00b5,
+	0x00ba,
+	0x02ec,
+	0x02ee,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x0559,
+	0x06d5,
+	0x06ff,
+	0x0710,
+	0x07b1,
+	0x07fa,
+	0x081a,
+	0x0824,
+	0x0828,
+	0x093d,
+	0x0950,
+	0x09b2,
+	0x09bd,
+	0x09ce,
+	0x0a5e,
+	0x0abd,
+	0x0ad0,
+	0x0b3d,
+	0x0b71,
+	0x0b83,
+	0x0b9c,
+	0x0bd0,
+	0x0c3d,
+	0x0cbd,
+	0x0cde,
+	0x0d3d,
+	0x0d4e,
+	0x0dbd,
+	0x0e84,
+	0x0e8a,
+	0x0e8d,
+	0x0ea5,
+	0x0ea7,
+	0x0ebd,
+	0x0ec6,
+	0x0f00,
+	0x103f,
+	0x1061,
+	0x108e,
+	0x10c7,
+	0x10cd,
+	0x1258,
+	0x12c0,
+	0x17d7,
+	0x17dc,
+	0x18aa,
+	0x1aa7,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1fbe,
+	0x2071,
+	0x207f,
+	0x2102,
+	0x2107,
+	0x2115,
+	0x2124,
+	0x2126,
+	0x2128,
+	0x214e,
+	0x2d27,
+	0x2d2d,
+	0x2d6f,
+	0x2e2f,
+	0xa8fb,
+	0xa9cf,
+	0xaa7a,
+	0xaab1,
+	0xaac0,
+	0xaac2,
+	0xfb1d,
+	0xfb3e,
+	0x10808,
+	0x1083c,
+	0x10a00,
+	0x11176,
+	0x111da,
+	0x1133d,
+	0x114c7,
+	0x11644,
+	0x118ff,
+	0x16f50,
+	0x1d4a2,
+	0x1d4bb,
+	0x1d546,
+	0x1ee24,
+	0x1ee27,
+	0x1ee39,
+	0x1ee3b,
+	0x1ee42,
+	0x1ee47,
+	0x1ee49,
+	0x1ee4b,
+	0x1ee54,
+	0x1ee57,
+	0x1ee59,
+	0x1ee5b,
+	0x1ee5d,
+	0x1ee5f,
+	0x1ee64,
+	0x1ee7e,
+};
+
+bool isalpha(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, isalphar, nelem (isalphar)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = rbsearch(c, isalphas, nelem (isalphas), 1);
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+static char32_t isupperr[] = {
+	0x0041, 0x005a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00de,
+	0x0178, 0x0179,
+	0x0181, 0x0182,
+	0x0186, 0x0187,
+	0x0189, 0x018b,
+	0x018e, 0x0191,
+	0x0193, 0x0194,
+	0x0196, 0x0198,
+	0x019c, 0x019d,
+	0x019f, 0x01a0,
+	0x01a6, 0x01a7,
+	0x01ae, 0x01af,
+	0x01b1, 0x01b3,
+	0x01b7, 0x01b8,
+	0x01f6, 0x01f8,
+	0x023a, 0x023b,
+	0x023d, 0x023e,
+	0x0243, 0x0246,
+	0x0388, 0x038a,
+	0x038e, 0x038f,
+	0x0391, 0x03a1,
+	0x03a3, 0x03ab,
+	0x03d2, 0x03d4,
+	0x03f9, 0x03fa,
+	0x03fd, 0x042f,
+	0x04c0, 0x04c1,
+	0x0531, 0x0556,
+	0x10a0, 0x10c5,
+	0x1f08, 0x1f0f,
+	0x1f18, 0x1f1d,
+	0x1f28, 0x1f2f,
+	0x1f38, 0x1f3f,
+	0x1f48, 0x1f4d,
+	0x1f68, 0x1f6f,
+	0x1f88, 0x1f8f,
+	0x1f98, 0x1f9f,
+	0x1fa8, 0x1faf,
+	0x1fb8, 0x1fbc,
+	0x1fc8, 0x1fcc,
+	0x1fd8, 0x1fdb,
+	0x1fe8, 0x1fec,
+	0x1ff8, 0x1ffc,
+	0x210b, 0x210d,
+	0x2110, 0x2112,
+	0x2119, 0x211d,
+	0x212a, 0x212d,
+	0x2130, 0x2133,
+	0x213e, 0x213f,
+	0x2160, 0x216f,
+	0x24b6, 0x24cf,
+	0x2c00, 0x2c2e,
+	0x2c62, 0x2c64,
+	0x2c6d, 0x2c70,
+	0x2c7e, 0x2c80,
+	0xa77d, 0xa77e,
+	0xa7aa, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xff21, 0xff3a,
+	0x10400, 0x10427,
+	0x118a0, 0x118bf,
+	0x1d400, 0x1d419,
+	0x1d434, 0x1d44d,
+	0x1d468, 0x1d481,
+	0x1d49e, 0x1d49f,
+	0x1d4a5, 0x1d4a6,
+	0x1d4a9, 0x1d4ac,
+	0x1d4ae, 0x1d4b5,
+	0x1d4d0, 0x1d4e9,
+	0x1d504, 0x1d505,
+	0x1d507, 0x1d50a,
+	0x1d50d, 0x1d514,
+	0x1d516, 0x1d51c,
+	0x1d538, 0x1d539,
+	0x1d53b, 0x1d53e,
+	0x1d540, 0x1d544,
+	0x1d54a, 0x1d550,
+	0x1d56c, 0x1d585,
+	0x1d5a0, 0x1d5b9,
+	0x1d5d4, 0x1d5ed,
+	0x1d608, 0x1d621,
+	0x1d63c, 0x1d655,
+	0x1d670, 0x1d689,
+	0x1d6a8, 0x1d6c0,
+	0x1d6e2, 0x1d6fa,
+	0x1d71c, 0x1d734,
+	0x1d756, 0x1d76e,
+	0x1d790, 0x1d7a8,
+};
+
+static char32_t isuppers[] = {
+	0x0100,
+	0x0102,
+	0x0104,
+	0x0106,
+	0x0108,
+	0x010a,
+	0x010c,
+	0x010e,
+	0x0110,
+	0x0112,
+	0x0114,
+	0x0116,
+	0x0118,
+	0x011a,
+	0x011c,
+	0x011e,
+	0x0120,
+	0x0122,
+	0x0124,
+	0x0126,
+	0x0128,
+	0x012a,
+	0x012c,
+	0x012e,
+	0x0130,
+	0x0132,
+	0x0134,
+	0x0136,
+	0x0139,
+	0x013b,
+	0x013d,
+	0x013f,
+	0x0141,
+	0x0143,
+	0x0145,
+	0x0147,
+	0x014a,
+	0x014c,
+	0x014e,
+	0x0150,
+	0x0152,
+	0x0154,
+	0x0156,
+	0x0158,
+	0x015a,
+	0x015c,
+	0x015e,
+	0x0160,
+	0x0162,
+	0x0164,
+	0x0166,
+	0x0168,
+	0x016a,
+	0x016c,
+	0x016e,
+	0x0170,
+	0x0172,
+	0x0174,
+	0x0176,
+	0x017b,
+	0x017d,
+	0x0184,
+	0x01a2,
+	0x01a4,
+	0x01a9,
+	0x01ac,
+	0x01b5,
+	0x01bc,
+	0x01c4,
+	0x01c7,
+	0x01ca,
+	0x01cd,
+	0x01cf,
+	0x01d1,
+	0x01d3,
+	0x01d5,
+	0x01d7,
+	0x01d9,
+	0x01db,
+	0x01de,
+	0x01e0,
+	0x01e2,
+	0x01e4,
+	0x01e6,
+	0x01e8,
+	0x01ea,
+	0x01ec,
+	0x01ee,
+	0x01f1,
+	0x01f4,
+	0x01fa,
+	0x01fc,
+	0x01fe,
+	0x0200,
+	0x0202,
+	0x0204,
+	0x0206,
+	0x0208,
+	0x020a,
+	0x020c,
+	0x020e,
+	0x0210,
+	0x0212,
+	0x0214,
+	0x0216,
+	0x0218,
+	0x021a,
+	0x021c,
+	0x021e,
+	0x0220,
+	0x0222,
+	0x0224,
+	0x0226,
+	0x0228,
+	0x022a,
+	0x022c,
+	0x022e,
+	0x0230,
+	0x0232,
+	0x0241,
+	0x0248,
+	0x024a,
+	0x024c,
+	0x024e,
+	0x0370,
+	0x0372,
+	0x0376,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x03cf,
+	0x03d8,
+	0x03da,
+	0x03dc,
+	0x03de,
+	0x03e0,
+	0x03e2,
+	0x03e4,
+	0x03e6,
+	0x03e8,
+	0x03ea,
+	0x03ec,
+	0x03ee,
+	0x03f4,
+	0x03f7,
+	0x0460,
+	0x0462,
+	0x0464,
+	0x0466,
+	0x0468,
+	0x046a,
+	0x046c,
+	0x046e,
+	0x0470,
+	0x0472,
+	0x0474,
+	0x0476,
+	0x0478,
+	0x047a,
+	0x047c,
+	0x047e,
+	0x0480,
+	0x048a,
+	0x048c,
+	0x048e,
+	0x0490,
+	0x0492,
+	0x0494,
+	0x0496,
+	0x0498,
+	0x049a,
+	0x049c,
+	0x049e,
+	0x04a0,
+	0x04a2,
+	0x04a4,
+	0x04a6,
+	0x04a8,
+	0x04aa,
+	0x04ac,
+	0x04ae,
+	0x04b0,
+	0x04b2,
+	0x04b4,
+	0x04b6,
+	0x04b8,
+	0x04ba,
+	0x04bc,
+	0x04be,
+	0x04c3,
+	0x04c5,
+	0x04c7,
+	0x04c9,
+	0x04cb,
+	0x04cd,
+	0x04d0,
+	0x04d2,
+	0x04d4,
+	0x04d6,
+	0x04d8,
+	0x04da,
+	0x04dc,
+	0x04de,
+	0x04e0,
+	0x04e2,
+	0x04e4,
+	0x04e6,
+	0x04e8,
+	0x04ea,
+	0x04ec,
+	0x04ee,
+	0x04f0,
+	0x04f2,
+	0x04f4,
+	0x04f6,
+	0x04f8,
+	0x04fa,
+	0x04fc,
+	0x04fe,
+	0x0500,
+	0x0502,
+	0x0504,
+	0x0506,
+	0x0508,
+	0x050a,
+	0x050c,
+	0x050e,
+	0x0510,
+	0x0512,
+	0x0514,
+	0x0516,
+	0x0518,
+	0x051a,
+	0x051c,
+	0x051e,
+	0x0520,
+	0x0522,
+	0x0524,
+	0x0526,
+	0x0528,
+	0x052a,
+	0x052c,
+	0x052e,
+	0x10c7,
+	0x10cd,
+	0x1e00,
+	0x1e02,
+	0x1e04,
+	0x1e06,
+	0x1e08,
+	0x1e0a,
+	0x1e0c,
+	0x1e0e,
+	0x1e10,
+	0x1e12,
+	0x1e14,
+	0x1e16,
+	0x1e18,
+	0x1e1a,
+	0x1e1c,
+	0x1e1e,
+	0x1e20,
+	0x1e22,
+	0x1e24,
+	0x1e26,
+	0x1e28,
+	0x1e2a,
+	0x1e2c,
+	0x1e2e,
+	0x1e30,
+	0x1e32,
+	0x1e34,
+	0x1e36,
+	0x1e38,
+	0x1e3a,
+	0x1e3c,
+	0x1e3e,
+	0x1e40,
+	0x1e42,
+	0x1e44,
+	0x1e46,
+	0x1e48,
+	0x1e4a,
+	0x1e4c,
+	0x1e4e,
+	0x1e50,
+	0x1e52,
+	0x1e54,
+	0x1e56,
+	0x1e58,
+	0x1e5a,
+	0x1e5c,
+	0x1e5e,
+	0x1e60,
+	0x1e62,
+	0x1e64,
+	0x1e66,
+	0x1e68,
+	0x1e6a,
+	0x1e6c,
+	0x1e6e,
+	0x1e70,
+	0x1e72,
+	0x1e74,
+	0x1e76,
+	0x1e78,
+	0x1e7a,
+	0x1e7c,
+	0x1e7e,
+	0x1e80,
+	0x1e82,
+	0x1e84,
+	0x1e86,
+	0x1e88,
+	0x1e8a,
+	0x1e8c,
+	0x1e8e,
+	0x1e90,
+	0x1e92,
+	0x1e94,
+	0x1e9e,
+	0x1ea0,
+	0x1ea2,
+	0x1ea4,
+	0x1ea6,
+	0x1ea8,
+	0x1eaa,
+	0x1eac,
+	0x1eae,
+	0x1eb0,
+	0x1eb2,
+	0x1eb4,
+	0x1eb6,
+	0x1eb8,
+	0x1eba,
+	0x1ebc,
+	0x1ebe,
+	0x1ec0,
+	0x1ec2,
+	0x1ec4,
+	0x1ec6,
+	0x1ec8,
+	0x1eca,
+	0x1ecc,
+	0x1ece,
+	0x1ed0,
+	0x1ed2,
+	0x1ed4,
+	0x1ed6,
+	0x1ed8,
+	0x1eda,
+	0x1edc,
+	0x1ede,
+	0x1ee0,
+	0x1ee2,
+	0x1ee4,
+	0x1ee6,
+	0x1ee8,
+	0x1eea,
+	0x1eec,
+	0x1eee,
+	0x1ef0,
+	0x1ef2,
+	0x1ef4,
+	0x1ef6,
+	0x1ef8,
+	0x1efa,
+	0x1efc,
+	0x1efe,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1f5f,
+	0x2102,
+	0x2107,
+	0x2115,
+	0x2124,
+	0x2126,
+	0x2128,
+	0x2145,
+	0x2183,
+	0x2c60,
+	0x2c67,
+	0x2c69,
+	0x2c6b,
+	0x2c72,
+	0x2c75,
+	0x2c82,
+	0x2c84,
+	0x2c86,
+	0x2c88,
+	0x2c8a,
+	0x2c8c,
+	0x2c8e,
+	0x2c90,
+	0x2c92,
+	0x2c94,
+	0x2c96,
+	0x2c98,
+	0x2c9a,
+	0x2c9c,
+	0x2c9e,
+	0x2ca0,
+	0x2ca2,
+	0x2ca4,
+	0x2ca6,
+	0x2ca8,
+	0x2caa,
+	0x2cac,
+	0x2cae,
+	0x2cb0,
+	0x2cb2,
+	0x2cb4,
+	0x2cb6,
+	0x2cb8,
+	0x2cba,
+	0x2cbc,
+	0x2cbe,
+	0x2cc0,
+	0x2cc2,
+	0x2cc4,
+	0x2cc6,
+	0x2cc8,
+	0x2cca,
+	0x2ccc,
+	0x2cce,
+	0x2cd0,
+	0x2cd2,
+	0x2cd4,
+	0x2cd6,
+	0x2cd8,
+	0x2cda,
+	0x2cdc,
+	0x2cde,
+	0x2ce0,
+	0x2ce2,
+	0x2ceb,
+	0x2ced,
+	0x2cf2,
+	0xa640,
+	0xa642,
+	0xa644,
+	0xa646,
+	0xa648,
+	0xa64a,
+	0xa64c,
+	0xa64e,
+	0xa650,
+	0xa652,
+	0xa654,
+	0xa656,
+	0xa658,
+	0xa65a,
+	0xa65c,
+	0xa65e,
+	0xa660,
+	0xa662,
+	0xa664,
+	0xa666,
+	0xa668,
+	0xa66a,
+	0xa66c,
+	0xa680,
+	0xa682,
+	0xa684,
+	0xa686,
+	0xa688,
+	0xa68a,
+	0xa68c,
+	0xa68e,
+	0xa690,
+	0xa692,
+	0xa694,
+	0xa696,
+	0xa698,
+	0xa69a,
+	0xa722,
+	0xa724,
+	0xa726,
+	0xa728,
+	0xa72a,
+	0xa72c,
+	0xa72e,
+	0xa732,
+	0xa734,
+	0xa736,
+	0xa738,
+	0xa73a,
+	0xa73c,
+	0xa73e,
+	0xa740,
+	0xa742,
+	0xa744,
+	0xa746,
+	0xa748,
+	0xa74a,
+	0xa74c,
+	0xa74e,
+	0xa750,
+	0xa752,
+	0xa754,
+	0xa756,
+	0xa758,
+	0xa75a,
+	0xa75c,
+	0xa75e,
+	0xa760,
+	0xa762,
+	0xa764,
+	0xa766,
+	0xa768,
+	0xa76a,
+	0xa76c,
+	0xa76e,
+	0xa779,
+	0xa77b,
+	0xa780,
+	0xa782,
+	0xa784,
+	0xa786,
+	0xa78b,
+	0xa78d,
+	0xa790,
+	0xa792,
+	0xa796,
+	0xa798,
+	0xa79a,
+	0xa79c,
+	0xa79e,
+	0xa7a0,
+	0xa7a2,
+	0xa7a4,
+	0xa7a6,
+	0xa7a8,
+	0x1d49c,
+	0x1d4a2,
+	0x1d546,
+	0x1d7ca,
+};
+
+bool isupper(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, isupperr, nelem (isupperr)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = rbsearch(c, isuppers, nelem (isuppers), 1);
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+static char32_t islowerr[] = {
+	0x0061, 0x007a,
+	0x00df, 0x00f6,
+	0x00f8, 0x00ff,
+	0x0137, 0x0138,
+	0x0148, 0x0149,
+	0x017e, 0x0180,
+	0x018c, 0x018d,
+	0x0199, 0x019b,
+	0x01aa, 0x01ab,
+	0x01b9, 0x01ba,
+	0x01bd, 0x01bf,
+	0x01dc, 0x01dd,
+	0x01ef, 0x01f0,
+	0x0233, 0x0239,
+	0x023f, 0x0240,
+	0x024f, 0x0293,
+	0x0295, 0x02af,
+	0x037b, 0x037d,
+	0x03ac, 0x03ce,
+	0x03d0, 0x03d1,
+	0x03d5, 0x03d7,
+	0x03ef, 0x03f3,
+	0x03fb, 0x03fc,
+	0x0430, 0x045f,
+	0x04ce, 0x04cf,
+	0x0561, 0x0587,
+	0x1d00, 0x1d2b,
+	0x1d6b, 0x1d77,
+	0x1d79, 0x1d9a,
+	0x1e95, 0x1e9d,
+	0x1eff, 0x1f07,
+	0x1f10, 0x1f15,
+	0x1f20, 0x1f27,
+	0x1f30, 0x1f37,
+	0x1f40, 0x1f45,
+	0x1f50, 0x1f57,
+	0x1f60, 0x1f67,
+	0x1f70, 0x1f7d,
+	0x1f80, 0x1f87,
+	0x1f90, 0x1f97,
+	0x1fa0, 0x1fa7,
+	0x1fb0, 0x1fb4,
+	0x1fb6, 0x1fb7,
+	0x1fc2, 0x1fc4,
+	0x1fc6, 0x1fc7,
+	0x1fd0, 0x1fd3,
+	0x1fd6, 0x1fd7,
+	0x1fe0, 0x1fe7,
+	0x1ff2, 0x1ff4,
+	0x1ff6, 0x1ff7,
+	0x210e, 0x210f,
+	0x213c, 0x213d,
+	0x2146, 0x2149,
+	0x2170, 0x217f,
+	0x24d0, 0x24e9,
+	0x2c30, 0x2c5e,
+	0x2c65, 0x2c66,
+	0x2c73, 0x2c74,
+	0x2c76, 0x2c7b,
+	0x2ce3, 0x2ce4,
+	0x2d00, 0x2d25,
+	0xa72f, 0xa731,
+	0xa771, 0xa778,
+	0xa793, 0xa795,
+	0xab30, 0xab5a,
+	0xab64, 0xab65,
+	0xfb00, 0xfb06,
+	0xfb13, 0xfb17,
+	0xff41, 0xff5a,
+	0x10428, 0x1044f,
+	0x118c0, 0x118df,
+	0x1d41a, 0x1d433,
+	0x1d44e, 0x1d454,
+	0x1d456, 0x1d467,
+	0x1d482, 0x1d49b,
+	0x1d4b6, 0x1d4b9,
+	0x1d4bd, 0x1d4c3,
+	0x1d4c5, 0x1d4cf,
+	0x1d4ea, 0x1d503,
+	0x1d51e, 0x1d537,
+	0x1d552, 0x1d56b,
+	0x1d586, 0x1d59f,
+	0x1d5ba, 0x1d5d3,
+	0x1d5ee, 0x1d607,
+	0x1d622, 0x1d63b,
+	0x1d656, 0x1d66f,
+	0x1d68a, 0x1d6a5,
+	0x1d6c2, 0x1d6da,
+	0x1d6dc, 0x1d6e1,
+	0x1d6fc, 0x1d714,
+	0x1d716, 0x1d71b,
+	0x1d736, 0x1d74e,
+	0x1d750, 0x1d755,
+	0x1d770, 0x1d788,
+	0x1d78a, 0x1d78f,
+	0x1d7aa, 0x1d7c2,
+	0x1d7c4, 0x1d7c9,
+};
+
+static char32_t islowers[] = {
+	0x00b5,
+	0x0101,
+	0x0103,
+	0x0105,
+	0x0107,
+	0x0109,
+	0x010b,
+	0x010d,
+	0x010f,
+	0x0111,
+	0x0113,
+	0x0115,
+	0x0117,
+	0x0119,
+	0x011b,
+	0x011d,
+	0x011f,
+	0x0121,
+	0x0123,
+	0x0125,
+	0x0127,
+	0x0129,
+	0x012b,
+	0x012d,
+	0x012f,
+	0x0131,
+	0x0133,
+	0x0135,
+	0x013a,
+	0x013c,
+	0x013e,
+	0x0140,
+	0x0142,
+	0x0144,
+	0x0146,
+	0x014b,
+	0x014d,
+	0x014f,
+	0x0151,
+	0x0153,
+	0x0155,
+	0x0157,
+	0x0159,
+	0x015b,
+	0x015d,
+	0x015f,
+	0x0161,
+	0x0163,
+	0x0165,
+	0x0167,
+	0x0169,
+	0x016b,
+	0x016d,
+	0x016f,
+	0x0171,
+	0x0173,
+	0x0175,
+	0x0177,
+	0x017a,
+	0x017c,
+	0x0183,
+	0x0185,
+	0x0188,
+	0x0192,
+	0x0195,
+	0x019e,
+	0x01a1,
+	0x01a3,
+	0x01a5,
+	0x01a8,
+	0x01ad,
+	0x01b0,
+	0x01b4,
+	0x01b6,
+	0x01c6,
+	0x01c9,
+	0x01cc,
+	0x01ce,
+	0x01d0,
+	0x01d2,
+	0x01d4,
+	0x01d6,
+	0x01d8,
+	0x01da,
+	0x01df,
+	0x01e1,
+	0x01e3,
+	0x01e5,
+	0x01e7,
+	0x01e9,
+	0x01eb,
+	0x01ed,
+	0x01f3,
+	0x01f5,
+	0x01f9,
+	0x01fb,
+	0x01fd,
+	0x01ff,
+	0x0201,
+	0x0203,
+	0x0205,
+	0x0207,
+	0x0209,
+	0x020b,
+	0x020d,
+	0x020f,
+	0x0211,
+	0x0213,
+	0x0215,
+	0x0217,
+	0x0219,
+	0x021b,
+	0x021d,
+	0x021f,
+	0x0221,
+	0x0223,
+	0x0225,
+	0x0227,
+	0x0229,
+	0x022b,
+	0x022d,
+	0x022f,
+	0x0231,
+	0x023c,
+	0x0242,
+	0x0247,
+	0x0249,
+	0x024b,
+	0x024d,
+	0x0371,
+	0x0373,
+	0x0377,
+	0x0390,
+	0x03d9,
+	0x03db,
+	0x03dd,
+	0x03df,
+	0x03e1,
+	0x03e3,
+	0x03e5,
+	0x03e7,
+	0x03e9,
+	0x03eb,
+	0x03ed,
+	0x03f5,
+	0x03f8,
+	0x0461,
+	0x0463,
+	0x0465,
+	0x0467,
+	0x0469,
+	0x046b,
+	0x046d,
+	0x046f,
+	0x0471,
+	0x0473,
+	0x0475,
+	0x0477,
+	0x0479,
+	0x047b,
+	0x047d,
+	0x047f,
+	0x0481,
+	0x048b,
+	0x048d,
+	0x048f,
+	0x0491,
+	0x0493,
+	0x0495,
+	0x0497,
+	0x0499,
+	0x049b,
+	0x049d,
+	0x049f,
+	0x04a1,
+	0x04a3,
+	0x04a5,
+	0x04a7,
+	0x04a9,
+	0x04ab,
+	0x04ad,
+	0x04af,
+	0x04b1,
+	0x04b3,
+	0x04b5,
+	0x04b7,
+	0x04b9,
+	0x04bb,
+	0x04bd,
+	0x04bf,
+	0x04c2,
+	0x04c4,
+	0x04c6,
+	0x04c8,
+	0x04ca,
+	0x04cc,
+	0x04d1,
+	0x04d3,
+	0x04d5,
+	0x04d7,
+	0x04d9,
+	0x04db,
+	0x04dd,
+	0x04df,
+	0x04e1,
+	0x04e3,
+	0x04e5,
+	0x04e7,
+	0x04e9,
+	0x04eb,
+	0x04ed,
+	0x04ef,
+	0x04f1,
+	0x04f3,
+	0x04f5,
+	0x04f7,
+	0x04f9,
+	0x04fb,
+	0x04fd,
+	0x04ff,
+	0x0501,
+	0x0503,
+	0x0505,
+	0x0507,
+	0x0509,
+	0x050b,
+	0x050d,
+	0x050f,
+	0x0511,
+	0x0513,
+	0x0515,
+	0x0517,
+	0x0519,
+	0x051b,
+	0x051d,
+	0x051f,
+	0x0521,
+	0x0523,
+	0x0525,
+	0x0527,
+	0x0529,
+	0x052b,
+	0x052d,
+	0x052f,
+	0x1e01,
+	0x1e03,
+	0x1e05,
+	0x1e07,
+	0x1e09,
+	0x1e0b,
+	0x1e0d,
+	0x1e0f,
+	0x1e11,
+	0x1e13,
+	0x1e15,
+	0x1e17,
+	0x1e19,
+	0x1e1b,
+	0x1e1d,
+	0x1e1f,
+	0x1e21,
+	0x1e23,
+	0x1e25,
+	0x1e27,
+	0x1e29,
+	0x1e2b,
+	0x1e2d,
+	0x1e2f,
+	0x1e31,
+	0x1e33,
+	0x1e35,
+	0x1e37,
+	0x1e39,
+	0x1e3b,
+	0x1e3d,
+	0x1e3f,
+	0x1e41,
+	0x1e43,
+	0x1e45,
+	0x1e47,
+	0x1e49,
+	0x1e4b,
+	0x1e4d,
+	0x1e4f,
+	0x1e51,
+	0x1e53,
+	0x1e55,
+	0x1e57,
+	0x1e59,
+	0x1e5b,
+	0x1e5d,
+	0x1e5f,
+	0x1e61,
+	0x1e63,
+	0x1e65,
+	0x1e67,
+	0x1e69,
+	0x1e6b,
+	0x1e6d,
+	0x1e6f,
+	0x1e71,
+	0x1e73,
+	0x1e75,
+	0x1e77,
+	0x1e79,
+	0x1e7b,
+	0x1e7d,
+	0x1e7f,
+	0x1e81,
+	0x1e83,
+	0x1e85,
+	0x1e87,
+	0x1e89,
+	0x1e8b,
+	0x1e8d,
+	0x1e8f,
+	0x1e91,
+	0x1e93,
+	0x1e9f,
+	0x1ea1,
+	0x1ea3,
+	0x1ea5,
+	0x1ea7,
+	0x1ea9,
+	0x1eab,
+	0x1ead,
+	0x1eaf,
+	0x1eb1,
+	0x1eb3,
+	0x1eb5,
+	0x1eb7,
+	0x1eb9,
+	0x1ebb,
+	0x1ebd,
+	0x1ebf,
+	0x1ec1,
+	0x1ec3,
+	0x1ec5,
+	0x1ec7,
+	0x1ec9,
+	0x1ecb,
+	0x1ecd,
+	0x1ecf,
+	0x1ed1,
+	0x1ed3,
+	0x1ed5,
+	0x1ed7,
+	0x1ed9,
+	0x1edb,
+	0x1edd,
+	0x1edf,
+	0x1ee1,
+	0x1ee3,
+	0x1ee5,
+	0x1ee7,
+	0x1ee9,
+	0x1eeb,
+	0x1eed,
+	0x1eef,
+	0x1ef1,
+	0x1ef3,
+	0x1ef5,
+	0x1ef7,
+	0x1ef9,
+	0x1efb,
+	0x1efd,
+	0x1fbe,
+	0x210a,
+	0x2113,
+	0x212f,
+	0x2134,
+	0x2139,
+	0x214e,
+	0x2184,
+	0x2c61,
+	0x2c68,
+	0x2c6a,
+	0x2c6c,
+	0x2c71,
+	0x2c81,
+	0x2c83,
+	0x2c85,
+	0x2c87,
+	0x2c89,
+	0x2c8b,
+	0x2c8d,
+	0x2c8f,
+	0x2c91,
+	0x2c93,
+	0x2c95,
+	0x2c97,
+	0x2c99,
+	0x2c9b,
+	0x2c9d,
+	0x2c9f,
+	0x2ca1,
+	0x2ca3,
+	0x2ca5,
+	0x2ca7,
+	0x2ca9,
+	0x2cab,
+	0x2cad,
+	0x2caf,
+	0x2cb1,
+	0x2cb3,
+	0x2cb5,
+	0x2cb7,
+	0x2cb9,
+	0x2cbb,
+	0x2cbd,
+	0x2cbf,
+	0x2cc1,
+	0x2cc3,
+	0x2cc5,
+	0x2cc7,
+	0x2cc9,
+	0x2ccb,
+	0x2ccd,
+	0x2ccf,
+	0x2cd1,
+	0x2cd3,
+	0x2cd5,
+	0x2cd7,
+	0x2cd9,
+	0x2cdb,
+	0x2cdd,
+	0x2cdf,
+	0x2ce1,
+	0x2cec,
+	0x2cee,
+	0x2cf3,
+	0x2d27,
+	0x2d2d,
+	0xa641,
+	0xa643,
+	0xa645,
+	0xa647,
+	0xa649,
+	0xa64b,
+	0xa64d,
+	0xa64f,
+	0xa651,
+	0xa653,
+	0xa655,
+	0xa657,
+	0xa659,
+	0xa65b,
+	0xa65d,
+	0xa65f,
+	0xa661,
+	0xa663,
+	0xa665,
+	0xa667,
+	0xa669,
+	0xa66b,
+	0xa66d,
+	0xa681,
+	0xa683,
+	0xa685,
+	0xa687,
+	0xa689,
+	0xa68b,
+	0xa68d,
+	0xa68f,
+	0xa691,
+	0xa693,
+	0xa695,
+	0xa697,
+	0xa699,
+	0xa69b,
+	0xa723,
+	0xa725,
+	0xa727,
+	0xa729,
+	0xa72b,
+	0xa72d,
+	0xa733,
+	0xa735,
+	0xa737,
+	0xa739,
+	0xa73b,
+	0xa73d,
+	0xa73f,
+	0xa741,
+	0xa743,
+	0xa745,
+	0xa747,
+	0xa749,
+	0xa74b,
+	0xa74d,
+	0xa74f,
+	0xa751,
+	0xa753,
+	0xa755,
+	0xa757,
+	0xa759,
+	0xa75b,
+	0xa75d,
+	0xa75f,
+	0xa761,
+	0xa763,
+	0xa765,
+	0xa767,
+	0xa769,
+	0xa76b,
+	0xa76d,
+	0xa76f,
+	0xa77a,
+	0xa77c,
+	0xa77f,
+	0xa781,
+	0xa783,
+	0xa785,
+	0xa787,
+	0xa78c,
+	0xa78e,
+	0xa791,
+	0xa797,
+	0xa799,
+	0xa79b,
+	0xa79d,
+	0xa79f,
+	0xa7a1,
+	0xa7a3,
+	0xa7a5,
+	0xa7a7,
+	0xa7a9,
+	0xa7fa,
+	0x1d4bb,
+	0x1d7cb,
+};
+
+bool islower(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, islowerr, nelem (islowerr)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = rbsearch(c, islowers, nelem (islowers), 1);
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+static char32_t istitler[] = {
+	0x0041, 0x005a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00de,
+	0x0178, 0x0179,
+	0x0181, 0x0182,
+	0x0186, 0x0187,
+	0x0189, 0x018b,
+	0x018e, 0x0191,
+	0x0193, 0x0194,
+	0x0196, 0x0198,
+	0x019c, 0x019d,
+	0x019f, 0x01a0,
+	0x01a6, 0x01a7,
+	0x01ae, 0x01af,
+	0x01b1, 0x01b3,
+	0x01b7, 0x01b8,
+	0x01f6, 0x01f8,
+	0x023a, 0x023b,
+	0x023d, 0x023e,
+	0x0243, 0x0246,
+	0x0388, 0x038a,
+	0x038e, 0x038f,
+	0x0391, 0x03a1,
+	0x03a3, 0x03ab,
+	0x03f9, 0x03fa,
+	0x03fd, 0x042f,
+	0x04c0, 0x04c1,
+	0x0531, 0x0556,
+	0x10a0, 0x10c5,
+	0x1f08, 0x1f0f,
+	0x1f18, 0x1f1d,
+	0x1f28, 0x1f2f,
+	0x1f38, 0x1f3f,
+	0x1f48, 0x1f4d,
+	0x1f68, 0x1f6f,
+	0x1f88, 0x1f8f,
+	0x1f98, 0x1f9f,
+	0x1fa8, 0x1faf,
+	0x1fb8, 0x1fbc,
+	0x1fc8, 0x1fcc,
+	0x1fd8, 0x1fdb,
+	0x1fe8, 0x1fec,
+	0x1ff8, 0x1ffc,
+	0x2160, 0x216f,
+	0x24b6, 0x24cf,
+	0x2c00, 0x2c2e,
+	0x2c62, 0x2c64,
+	0x2c6d, 0x2c70,
+	0x2c7e, 0x2c80,
+	0xa77d, 0xa77e,
+	0xa7aa, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xff21, 0xff3a,
+	0x10400, 0x10427,
+	0x118a0, 0x118bf,
+};
+
+static char32_t istitles[] = {
+	0x0100,
+	0x0102,
+	0x0104,
+	0x0106,
+	0x0108,
+	0x010a,
+	0x010c,
+	0x010e,
+	0x0110,
+	0x0112,
+	0x0114,
+	0x0116,
+	0x0118,
+	0x011a,
+	0x011c,
+	0x011e,
+	0x0120,
+	0x0122,
+	0x0124,
+	0x0126,
+	0x0128,
+	0x012a,
+	0x012c,
+	0x012e,
+	0x0132,
+	0x0134,
+	0x0136,
+	0x0139,
+	0x013b,
+	0x013d,
+	0x013f,
+	0x0141,
+	0x0143,
+	0x0145,
+	0x0147,
+	0x014a,
+	0x014c,
+	0x014e,
+	0x0150,
+	0x0152,
+	0x0154,
+	0x0156,
+	0x0158,
+	0x015a,
+	0x015c,
+	0x015e,
+	0x0160,
+	0x0162,
+	0x0164,
+	0x0166,
+	0x0168,
+	0x016a,
+	0x016c,
+	0x016e,
+	0x0170,
+	0x0172,
+	0x0174,
+	0x0176,
+	0x017b,
+	0x017d,
+	0x0184,
+	0x01a2,
+	0x01a4,
+	0x01a9,
+	0x01ac,
+	0x01b5,
+	0x01bc,
+	0x01c5,
+	0x01c8,
+	0x01cb,
+	0x01cd,
+	0x01cf,
+	0x01d1,
+	0x01d3,
+	0x01d5,
+	0x01d7,
+	0x01d9,
+	0x01db,
+	0x01de,
+	0x01e0,
+	0x01e2,
+	0x01e4,
+	0x01e6,
+	0x01e8,
+	0x01ea,
+	0x01ec,
+	0x01ee,
+	0x01f2,
+	0x01f4,
+	0x01fa,
+	0x01fc,
+	0x01fe,
+	0x0200,
+	0x0202,
+	0x0204,
+	0x0206,
+	0x0208,
+	0x020a,
+	0x020c,
+	0x020e,
+	0x0210,
+	0x0212,
+	0x0214,
+	0x0216,
+	0x0218,
+	0x021a,
+	0x021c,
+	0x021e,
+	0x0220,
+	0x0222,
+	0x0224,
+	0x0226,
+	0x0228,
+	0x022a,
+	0x022c,
+	0x022e,
+	0x0230,
+	0x0232,
+	0x0241,
+	0x0248,
+	0x024a,
+	0x024c,
+	0x024e,
+	0x0370,
+	0x0372,
+	0x0376,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x03cf,
+	0x03d8,
+	0x03da,
+	0x03dc,
+	0x03de,
+	0x03e0,
+	0x03e2,
+	0x03e4,
+	0x03e6,
+	0x03e8,
+	0x03ea,
+	0x03ec,
+	0x03ee,
+	0x03f7,
+	0x0460,
+	0x0462,
+	0x0464,
+	0x0466,
+	0x0468,
+	0x046a,
+	0x046c,
+	0x046e,
+	0x0470,
+	0x0472,
+	0x0474,
+	0x0476,
+	0x0478,
+	0x047a,
+	0x047c,
+	0x047e,
+	0x0480,
+	0x048a,
+	0x048c,
+	0x048e,
+	0x0490,
+	0x0492,
+	0x0494,
+	0x0496,
+	0x0498,
+	0x049a,
+	0x049c,
+	0x049e,
+	0x04a0,
+	0x04a2,
+	0x04a4,
+	0x04a6,
+	0x04a8,
+	0x04aa,
+	0x04ac,
+	0x04ae,
+	0x04b0,
+	0x04b2,
+	0x04b4,
+	0x04b6,
+	0x04b8,
+	0x04ba,
+	0x04bc,
+	0x04be,
+	0x04c3,
+	0x04c5,
+	0x04c7,
+	0x04c9,
+	0x04cb,
+	0x04cd,
+	0x04d0,
+	0x04d2,
+	0x04d4,
+	0x04d6,
+	0x04d8,
+	0x04da,
+	0x04dc,
+	0x04de,
+	0x04e0,
+	0x04e2,
+	0x04e4,
+	0x04e6,
+	0x04e8,
+	0x04ea,
+	0x04ec,
+	0x04ee,
+	0x04f0,
+	0x04f2,
+	0x04f4,
+	0x04f6,
+	0x04f8,
+	0x04fa,
+	0x04fc,
+	0x04fe,
+	0x0500,
+	0x0502,
+	0x0504,
+	0x0506,
+	0x0508,
+	0x050a,
+	0x050c,
+	0x050e,
+	0x0510,
+	0x0512,
+	0x0514,
+	0x0516,
+	0x0518,
+	0x051a,
+	0x051c,
+	0x051e,
+	0x0520,
+	0x0522,
+	0x0524,
+	0x0526,
+	0x0528,
+	0x052a,
+	0x052c,
+	0x052e,
+	0x10c7,
+	0x10cd,
+	0x1e00,
+	0x1e02,
+	0x1e04,
+	0x1e06,
+	0x1e08,
+	0x1e0a,
+	0x1e0c,
+	0x1e0e,
+	0x1e10,
+	0x1e12,
+	0x1e14,
+	0x1e16,
+	0x1e18,
+	0x1e1a,
+	0x1e1c,
+	0x1e1e,
+	0x1e20,
+	0x1e22,
+	0x1e24,
+	0x1e26,
+	0x1e28,
+	0x1e2a,
+	0x1e2c,
+	0x1e2e,
+	0x1e30,
+	0x1e32,
+	0x1e34,
+	0x1e36,
+	0x1e38,
+	0x1e3a,
+	0x1e3c,
+	0x1e3e,
+	0x1e40,
+	0x1e42,
+	0x1e44,
+	0x1e46,
+	0x1e48,
+	0x1e4a,
+	0x1e4c,
+	0x1e4e,
+	0x1e50,
+	0x1e52,
+	0x1e54,
+	0x1e56,
+	0x1e58,
+	0x1e5a,
+	0x1e5c,
+	0x1e5e,
+	0x1e60,
+	0x1e62,
+	0x1e64,
+	0x1e66,
+	0x1e68,
+	0x1e6a,
+	0x1e6c,
+	0x1e6e,
+	0x1e70,
+	0x1e72,
+	0x1e74,
+	0x1e76,
+	0x1e78,
+	0x1e7a,
+	0x1e7c,
+	0x1e7e,
+	0x1e80,
+	0x1e82,
+	0x1e84,
+	0x1e86,
+	0x1e88,
+	0x1e8a,
+	0x1e8c,
+	0x1e8e,
+	0x1e90,
+	0x1e92,
+	0x1e94,
+	0x1ea0,
+	0x1ea2,
+	0x1ea4,
+	0x1ea6,
+	0x1ea8,
+	0x1eaa,
+	0x1eac,
+	0x1eae,
+	0x1eb0,
+	0x1eb2,
+	0x1eb4,
+	0x1eb6,
+	0x1eb8,
+	0x1eba,
+	0x1ebc,
+	0x1ebe,
+	0x1ec0,
+	0x1ec2,
+	0x1ec4,
+	0x1ec6,
+	0x1ec8,
+	0x1eca,
+	0x1ecc,
+	0x1ece,
+	0x1ed0,
+	0x1ed2,
+	0x1ed4,
+	0x1ed6,
+	0x1ed8,
+	0x1eda,
+	0x1edc,
+	0x1ede,
+	0x1ee0,
+	0x1ee2,
+	0x1ee4,
+	0x1ee6,
+	0x1ee8,
+	0x1eea,
+	0x1eec,
+	0x1eee,
+	0x1ef0,
+	0x1ef2,
+	0x1ef4,
+	0x1ef6,
+	0x1ef8,
+	0x1efa,
+	0x1efc,
+	0x1efe,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1f5f,
+	0x2132,
+	0x2183,
+	0x2c60,
+	0x2c67,
+	0x2c69,
+	0x2c6b,
+	0x2c72,
+	0x2c75,
+	0x2c82,
+	0x2c84,
+	0x2c86,
+	0x2c88,
+	0x2c8a,
+	0x2c8c,
+	0x2c8e,
+	0x2c90,
+	0x2c92,
+	0x2c94,
+	0x2c96,
+	0x2c98,
+	0x2c9a,
+	0x2c9c,
+	0x2c9e,
+	0x2ca0,
+	0x2ca2,
+	0x2ca4,
+	0x2ca6,
+	0x2ca8,
+	0x2caa,
+	0x2cac,
+	0x2cae,
+	0x2cb0,
+	0x2cb2,
+	0x2cb4,
+	0x2cb6,
+	0x2cb8,
+	0x2cba,
+	0x2cbc,
+	0x2cbe,
+	0x2cc0,
+	0x2cc2,
+	0x2cc4,
+	0x2cc6,
+	0x2cc8,
+	0x2cca,
+	0x2ccc,
+	0x2cce,
+	0x2cd0,
+	0x2cd2,
+	0x2cd4,
+	0x2cd6,
+	0x2cd8,
+	0x2cda,
+	0x2cdc,
+	0x2cde,
+	0x2ce0,
+	0x2ce2,
+	0x2ceb,
+	0x2ced,
+	0x2cf2,
+	0xa640,
+	0xa642,
+	0xa644,
+	0xa646,
+	0xa648,
+	0xa64a,
+	0xa64c,
+	0xa64e,
+	0xa650,
+	0xa652,
+	0xa654,
+	0xa656,
+	0xa658,
+	0xa65a,
+	0xa65c,
+	0xa65e,
+	0xa660,
+	0xa662,
+	0xa664,
+	0xa666,
+	0xa668,
+	0xa66a,
+	0xa66c,
+	0xa680,
+	0xa682,
+	0xa684,
+	0xa686,
+	0xa688,
+	0xa68a,
+	0xa68c,
+	0xa68e,
+	0xa690,
+	0xa692,
+	0xa694,
+	0xa696,
+	0xa698,
+	0xa69a,
+	0xa722,
+	0xa724,
+	0xa726,
+	0xa728,
+	0xa72a,
+	0xa72c,
+	0xa72e,
+	0xa732,
+	0xa734,
+	0xa736,
+	0xa738,
+	0xa73a,
+	0xa73c,
+	0xa73e,
+	0xa740,
+	0xa742,
+	0xa744,
+	0xa746,
+	0xa748,
+	0xa74a,
+	0xa74c,
+	0xa74e,
+	0xa750,
+	0xa752,
+	0xa754,
+	0xa756,
+	0xa758,
+	0xa75a,
+	0xa75c,
+	0xa75e,
+	0xa760,
+	0xa762,
+	0xa764,
+	0xa766,
+	0xa768,
+	0xa76a,
+	0xa76c,
+	0xa76e,
+	0xa779,
+	0xa77b,
+	0xa780,
+	0xa782,
+	0xa784,
+	0xa786,
+	0xa78b,
+	0xa78d,
+	0xa790,
+	0xa792,
+	0xa796,
+	0xa798,
+	0xa79a,
+	0xa79c,
+	0xa79e,
+	0xa7a0,
+	0xa7a2,
+	0xa7a4,
+	0xa7a6,
+	0xa7a8,
+};
+
+bool istitle(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, istitler, nelem (istitler)/2, 2);
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = rbsearch(c, istitles, nelem (istitles), 1);
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+char32_t toupperr[] = {
+	0x0061, 0x007a, 1048544,
+	0x00e0, 0x00f6, 1048544,
+	0x00f8, 0x00fe, 1048544,
+	0x023f, 0x0240, 1059391,
+	0x0256, 0x0257, 1048371,
+	0x028a, 0x028b, 1048359,
+	0x037b, 0x037d, 1048706,
+	0x03ad, 0x03af, 1048539,
+	0x03b1, 0x03c1, 1048544,
+	0x03c3, 0x03cb, 1048544,
+	0x03cd, 0x03ce, 1048513,
+	0x0430, 0x044f, 1048544,
+	0x0450, 0x045f, 1048496,
+	0x0561, 0x0586, 1048528,
+	0x1f00, 0x1f07, 1048584,
+	0x1f10, 0x1f15, 1048584,
+	0x1f20, 0x1f27, 1048584,
+	0x1f30, 0x1f37, 1048584,
+	0x1f40, 0x1f45, 1048584,
+	0x1f60, 0x1f67, 1048584,
+	0x1f70, 0x1f71, 1048650,
+	0x1f72, 0x1f75, 1048662,
+	0x1f76, 0x1f77, 1048676,
+	0x1f78, 0x1f79, 1048704,
+	0x1f7a, 0x1f7b, 1048688,
+	0x1f7c, 0x1f7d, 1048702,
+	0x1f80, 0x1f87, 1048584,
+	0x1f90, 0x1f97, 1048584,
+	0x1fa0, 0x1fa7, 1048584,
+	0x1fb0, 0x1fb1, 1048584,
+	0x1fd0, 0x1fd1, 1048584,
+	0x1fe0, 0x1fe1, 1048584,
+	0x2170, 0x217f, 1048560,
+	0x24d0, 0x24e9, 1048550,
+	0x2c30, 0x2c5e, 1048528,
+	0x2d00, 0x2d25, 1041312,
+	0xff41, 0xff5a, 1048544,
+	0x10428, 0x1044f, 1048536,
+	0x118c0, 0x118df, 1048544,
+};
+
+static char32_t touppers[] = {
+	0x00b5, 1049319,
+	0x00ff, 1048697,
+	0x0101, 1048575,
+	0x0103, 1048575,
+	0x0105, 1048575,
+	0x0107, 1048575,
+	0x0109, 1048575,
+	0x010b, 1048575,
+	0x010d, 1048575,
+	0x010f, 1048575,
+	0x0111, 1048575,
+	0x0113, 1048575,
+	0x0115, 1048575,
+	0x0117, 1048575,
+	0x0119, 1048575,
+	0x011b, 1048575,
+	0x011d, 1048575,
+	0x011f, 1048575,
+	0x0121, 1048575,
+	0x0123, 1048575,
+	0x0125, 1048575,
+	0x0127, 1048575,
+	0x0129, 1048575,
+	0x012b, 1048575,
+	0x012d, 1048575,
+	0x012f, 1048575,
+	0x0131, 1048344,
+	0x0133, 1048575,
+	0x0135, 1048575,
+	0x0137, 1048575,
+	0x013a, 1048575,
+	0x013c, 1048575,
+	0x013e, 1048575,
+	0x0140, 1048575,
+	0x0142, 1048575,
+	0x0144, 1048575,
+	0x0146, 1048575,
+	0x0148, 1048575,
+	0x014b, 1048575,
+	0x014d, 1048575,
+	0x014f, 1048575,
+	0x0151, 1048575,
+	0x0153, 1048575,
+	0x0155, 1048575,
+	0x0157, 1048575,
+	0x0159, 1048575,
+	0x015b, 1048575,
+	0x015d, 1048575,
+	0x015f, 1048575,
+	0x0161, 1048575,
+	0x0163, 1048575,
+	0x0165, 1048575,
+	0x0167, 1048575,
+	0x0169, 1048575,
+	0x016b, 1048575,
+	0x016d, 1048575,
+	0x016f, 1048575,
+	0x0171, 1048575,
+	0x0173, 1048575,
+	0x0175, 1048575,
+	0x0177, 1048575,
+	0x017a, 1048575,
+	0x017c, 1048575,
+	0x017e, 1048575,
+	0x017f, 1048276,
+	0x0180, 1048771,
+	0x0183, 1048575,
+	0x0185, 1048575,
+	0x0188, 1048575,
+	0x018c, 1048575,
+	0x0192, 1048575,
+	0x0195, 1048673,
+	0x0199, 1048575,
+	0x019a, 1048739,
+	0x019e, 1048706,
+	0x01a1, 1048575,
+	0x01a3, 1048575,
+	0x01a5, 1048575,
+	0x01a8, 1048575,
+	0x01ad, 1048575,
+	0x01b0, 1048575,
+	0x01b4, 1048575,
+	0x01b6, 1048575,
+	0x01b9, 1048575,
+	0x01bd, 1048575,
+	0x01bf, 1048632,
+	0x01c5, 1048575,
+	0x01c6, 1048574,
+	0x01c8, 1048575,
+	0x01c9, 1048574,
+	0x01cb, 1048575,
+	0x01cc, 1048574,
+	0x01ce, 1048575,
+	0x01d0, 1048575,
+	0x01d2, 1048575,
+	0x01d4, 1048575,
+	0x01d6, 1048575,
+	0x01d8, 1048575,
+	0x01da, 1048575,
+	0x01dc, 1048575,
+	0x01dd, 1048497,
+	0x01df, 1048575,
+	0x01e1, 1048575,
+	0x01e3, 1048575,
+	0x01e5, 1048575,
+	0x01e7, 1048575,
+	0x01e9, 1048575,
+	0x01eb, 1048575,
+	0x01ed, 1048575,
+	0x01ef, 1048575,
+	0x01f2, 1048575,
+	0x01f3, 1048574,
+	0x01f5, 1048575,
+	0x01f9, 1048575,
+	0x01fb, 1048575,
+	0x01fd, 1048575,
+	0x01ff, 1048575,
+	0x0201, 1048575,
+	0x0203, 1048575,
+	0x0205, 1048575,
+	0x0207, 1048575,
+	0x0209, 1048575,
+	0x020b, 1048575,
+	0x020d, 1048575,
+	0x020f, 1048575,
+	0x0211, 1048575,
+	0x0213, 1048575,
+	0x0215, 1048575,
+	0x0217, 1048575,
+	0x0219, 1048575,
+	0x021b, 1048575,
+	0x021d, 1048575,
+	0x021f, 1048575,
+	0x0223, 1048575,
+	0x0225, 1048575,
+	0x0227, 1048575,
+	0x0229, 1048575,
+	0x022b, 1048575,
+	0x022d, 1048575,
+	0x022f, 1048575,
+	0x0231, 1048575,
+	0x0233, 1048575,
+	0x023c, 1048575,
+	0x0242, 1048575,
+	0x0247, 1048575,
+	0x0249, 1048575,
+	0x024b, 1048575,
+	0x024d, 1048575,
+	0x024f, 1048575,
+	0x0250, 1059359,
+	0x0251, 1059356,
+	0x0252, 1059358,
+	0x0253, 1048366,
+	0x0254, 1048370,
+	0x0259, 1048374,
+	0x025b, 1048373,
+	0x025c, 1090895,
+	0x0260, 1048371,
+	0x0261, 1090891,
+	0x0263, 1048369,
+	0x0265, 1090856,
+	0x0266, 1090884,
+	0x0268, 1048367,
+	0x0269, 1048365,
+	0x026b, 1059319,
+	0x026c, 1090881,
+	0x026f, 1048365,
+	0x0271, 1059325,
+	0x0272, 1048363,
+	0x0275, 1048362,
+	0x027d, 1059303,
+	0x0280, 1048358,
+	0x0283, 1048358,
+	0x0287, 1090858,
+	0x0288, 1048358,
+	0x0289, 1048507,
+	0x028c, 1048505,
+	0x0292, 1048357,
+	0x029e, 1090834,
+	0x0345, 1048660,
+	0x0371, 1048575,
+	0x0373, 1048575,
+	0x0377, 1048575,
+	0x03ac, 1048538,
+	0x03c2, 1048545,
+	0x03cc, 1048512,
+	0x03d0, 1048514,
+	0x03d1, 1048519,
+	0x03d5, 1048529,
+	0x03d6, 1048522,
+	0x03d7, 1048568,
+	0x03d9, 1048575,
+	0x03db, 1048575,
+	0x03dd, 1048575,
+	0x03df, 1048575,
+	0x03e1, 1048575,
+	0x03e3, 1048575,
+	0x03e5, 1048575,
+	0x03e7, 1048575,
+	0x03e9, 1048575,
+	0x03eb, 1048575,
+	0x03ed, 1048575,
+	0x03ef, 1048575,
+	0x03f0, 1048490,
+	0x03f1, 1048496,
+	0x03f2, 1048583,
+	0x03f3, 1048460,
+	0x03f5, 1048480,
+	0x03f8, 1048575,
+	0x03fb, 1048575,
+	0x0461, 1048575,
+	0x0463, 1048575,
+	0x0465, 1048575,
+	0x0467, 1048575,
+	0x0469, 1048575,
+	0x046b, 1048575,
+	0x046d, 1048575,
+	0x046f, 1048575,
+	0x0471, 1048575,
+	0x0473, 1048575,
+	0x0475, 1048575,
+	0x0477, 1048575,
+	0x0479, 1048575,
+	0x047b, 1048575,
+	0x047d, 1048575,
+	0x047f, 1048575,
+	0x0481, 1048575,
+	0x048b, 1048575,
+	0x048d, 1048575,
+	0x048f, 1048575,
+	0x0491, 1048575,
+	0x0493, 1048575,
+	0x0495, 1048575,
+	0x0497, 1048575,
+	0x0499, 1048575,
+	0x049b, 1048575,
+	0x049d, 1048575,
+	0x049f, 1048575,
+	0x04a1, 1048575,
+	0x04a3, 1048575,
+	0x04a5, 1048575,
+	0x04a7, 1048575,
+	0x04a9, 1048575,
+	0x04ab, 1048575,
+	0x04ad, 1048575,
+	0x04af, 1048575,
+	0x04b1, 1048575,
+	0x04b3, 1048575,
+	0x04b5, 1048575,
+	0x04b7, 1048575,
+	0x04b9, 1048575,
+	0x04bb, 1048575,
+	0x04bd, 1048575,
+	0x04bf, 1048575,
+	0x04c2, 1048575,
+	0x04c4, 1048575,
+	0x04c6, 1048575,
+	0x04c8, 1048575,
+	0x04ca, 1048575,
+	0x04cc, 1048575,
+	0x04ce, 1048575,
+	0x04cf, 1048561,
+	0x04d1, 1048575,
+	0x04d3, 1048575,
+	0x04d5, 1048575,
+	0x04d7, 1048575,
+	0x04d9, 1048575,
+	0x04db, 1048575,
+	0x04dd, 1048575,
+	0x04df, 1048575,
+	0x04e1, 1048575,
+	0x04e3, 1048575,
+	0x04e5, 1048575,
+	0x04e7, 1048575,
+	0x04e9, 1048575,
+	0x04eb, 1048575,
+	0x04ed, 1048575,
+	0x04ef, 1048575,
+	0x04f1, 1048575,
+	0x04f3, 1048575,
+	0x04f5, 1048575,
+	0x04f7, 1048575,
+	0x04f9, 1048575,
+	0x04fb, 1048575,
+	0x04fd, 1048575,
+	0x04ff, 1048575,
+	0x0501, 1048575,
+	0x0503, 1048575,
+	0x0505, 1048575,
+	0x0507, 1048575,
+	0x0509, 1048575,
+	0x050b, 1048575,
+	0x050d, 1048575,
+	0x050f, 1048575,
+	0x0511, 1048575,
+	0x0513, 1048575,
+	0x0515, 1048575,
+	0x0517, 1048575,
+	0x0519, 1048575,
+	0x051b, 1048575,
+	0x051d, 1048575,
+	0x051f, 1048575,
+	0x0521, 1048575,
+	0x0523, 1048575,
+	0x0525, 1048575,
+	0x0527, 1048575,
+	0x0529, 1048575,
+	0x052b, 1048575,
+	0x052d, 1048575,
+	0x052f, 1048575,
+	0x1d79, 1083908,
+	0x1d7d, 1052390,
+	0x1e01, 1048575,
+	0x1e03, 1048575,
+	0x1e05, 1048575,
+	0x1e07, 1048575,
+	0x1e09, 1048575,
+	0x1e0b, 1048575,
+	0x1e0d, 1048575,
+	0x1e0f, 1048575,
+	0x1e11, 1048575,
+	0x1e13, 1048575,
+	0x1e15, 1048575,
+	0x1e17, 1048575,
+	0x1e19, 1048575,
+	0x1e1b, 1048575,
+	0x1e1d, 1048575,
+	0x1e1f, 1048575,
+	0x1e21, 1048575,
+	0x1e23, 1048575,
+	0x1e25, 1048575,
+	0x1e27, 1048575,
+	0x1e29, 1048575,
+	0x1e2b, 1048575,
+	0x1e2d, 1048575,
+	0x1e2f, 1048575,
+	0x1e31, 1048575,
+	0x1e33, 1048575,
+	0x1e35, 1048575,
+	0x1e37, 1048575,
+	0x1e39, 1048575,
+	0x1e3b, 1048575,
+	0x1e3d, 1048575,
+	0x1e3f, 1048575,
+	0x1e41, 1048575,
+	0x1e43, 1048575,
+	0x1e45, 1048575,
+	0x1e47, 1048575,
+	0x1e49, 1048575,
+	0x1e4b, 1048575,
+	0x1e4d, 1048575,
+	0x1e4f, 1048575,
+	0x1e51, 1048575,
+	0x1e53, 1048575,
+	0x1e55, 1048575,
+	0x1e57, 1048575,
+	0x1e59, 1048575,
+	0x1e5b, 1048575,
+	0x1e5d, 1048575,
+	0x1e5f, 1048575,
+	0x1e61, 1048575,
+	0x1e63, 1048575,
+	0x1e65, 1048575,
+	0x1e67, 1048575,
+	0x1e69, 1048575,
+	0x1e6b, 1048575,
+	0x1e6d, 1048575,
+	0x1e6f, 1048575,
+	0x1e71, 1048575,
+	0x1e73, 1048575,
+	0x1e75, 1048575,
+	0x1e77, 1048575,
+	0x1e79, 1048575,
+	0x1e7b, 1048575,
+	0x1e7d, 1048575,
+	0x1e7f, 1048575,
+	0x1e81, 1048575,
+	0x1e83, 1048575,
+	0x1e85, 1048575,
+	0x1e87, 1048575,
+	0x1e89, 1048575,
+	0x1e8b, 1048575,
+	0x1e8d, 1048575,
+	0x1e8f, 1048575,
+	0x1e91, 1048575,
+	0x1e93, 1048575,
+	0x1e95, 1048575,
+	0x1e9b, 1048517,
+	0x1ea1, 1048575,
+	0x1ea3, 1048575,
+	0x1ea5, 1048575,
+	0x1ea7, 1048575,
+	0x1ea9, 1048575,
+	0x1eab, 1048575,
+	0x1ead, 1048575,
+	0x1eaf, 1048575,
+	0x1eb1, 1048575,
+	0x1eb3, 1048575,
+	0x1eb5, 1048575,
+	0x1eb7, 1048575,
+	0x1eb9, 1048575,
+	0x1ebb, 1048575,
+	0x1ebd, 1048575,
+	0x1ebf, 1048575,
+	0x1ec1, 1048575,
+	0x1ec3, 1048575,
+	0x1ec5, 1048575,
+	0x1ec7, 1048575,
+	0x1ec9, 1048575,
+	0x1ecb, 1048575,
+	0x1ecd, 1048575,
+	0x1ecf, 1048575,
+	0x1ed1, 1048575,
+	0x1ed3, 1048575,
+	0x1ed5, 1048575,
+	0x1ed7, 1048575,
+	0x1ed9, 1048575,
+	0x1edb, 1048575,
+	0x1edd, 1048575,
+	0x1edf, 1048575,
+	0x1ee1, 1048575,
+	0x1ee3, 1048575,
+	0x1ee5, 1048575,
+	0x1ee7, 1048575,
+	0x1ee9, 1048575,
+	0x1eeb, 1048575,
+	0x1eed, 1048575,
+	0x1eef, 1048575,
+	0x1ef1, 1048575,
+	0x1ef3, 1048575,
+	0x1ef5, 1048575,
+	0x1ef7, 1048575,
+	0x1ef9, 1048575,
+	0x1efb, 1048575,
+	0x1efd, 1048575,
+	0x1eff, 1048575,
+	0x1f51, 1048584,
+	0x1f53, 1048584,
+	0x1f55, 1048584,
+	0x1f57, 1048584,
+	0x1fb3, 1048585,
+	0x1fbe, 1041371,
+	0x1fc3, 1048585,
+	0x1fe5, 1048583,
+	0x1ff3, 1048585,
+	0x214e, 1048548,
+	0x2184, 1048575,
+	0x2c61, 1048575,
+	0x2c65, 1037781,
+	0x2c66, 1037784,
+	0x2c68, 1048575,
+	0x2c6a, 1048575,
+	0x2c6c, 1048575,
+	0x2c73, 1048575,
+	0x2c76, 1048575,
+	0x2c81, 1048575,
+	0x2c83, 1048575,
+	0x2c85, 1048575,
+	0x2c87, 1048575,
+	0x2c89, 1048575,
+	0x2c8b, 1048575,
+	0x2c8d, 1048575,
+	0x2c8f, 1048575,
+	0x2c91, 1048575,
+	0x2c93, 1048575,
+	0x2c95, 1048575,
+	0x2c97, 1048575,
+	0x2c99, 1048575,
+	0x2c9b, 1048575,
+	0x2c9d, 1048575,
+	0x2c9f, 1048575,
+	0x2ca1, 1048575,
+	0x2ca3, 1048575,
+	0x2ca5, 1048575,
+	0x2ca7, 1048575,
+	0x2ca9, 1048575,
+	0x2cab, 1048575,
+	0x2cad, 1048575,
+	0x2caf, 1048575,
+	0x2cb1, 1048575,
+	0x2cb3, 1048575,
+	0x2cb5, 1048575,
+	0x2cb7, 1048575,
+	0x2cb9, 1048575,
+	0x2cbb, 1048575,
+	0x2cbd, 1048575,
+	0x2cbf, 1048575,
+	0x2cc1, 1048575,
+	0x2cc3, 1048575,
+	0x2cc5, 1048575,
+	0x2cc7, 1048575,
+	0x2cc9, 1048575,
+	0x2ccb, 1048575,
+	0x2ccd, 1048575,
+	0x2ccf, 1048575,
+	0x2cd1, 1048575,
+	0x2cd3, 1048575,
+	0x2cd5, 1048575,
+	0x2cd7, 1048575,
+	0x2cd9, 1048575,
+	0x2cdb, 1048575,
+	0x2cdd, 1048575,
+	0x2cdf, 1048575,
+	0x2ce1, 1048575,
+	0x2ce3, 1048575,
+	0x2cec, 1048575,
+	0x2cee, 1048575,
+	0x2cf3, 1048575,
+	0x2d27, 1041312,
+	0x2d2d, 1041312,
+	0xa641, 1048575,
+	0xa643, 1048575,
+	0xa645, 1048575,
+	0xa647, 1048575,
+	0xa649, 1048575,
+	0xa64b, 1048575,
+	0xa64d, 1048575,
+	0xa64f, 1048575,
+	0xa651, 1048575,
+	0xa653, 1048575,
+	0xa655, 1048575,
+	0xa657, 1048575,
+	0xa659, 1048575,
+	0xa65b, 1048575,
+	0xa65d, 1048575,
+	0xa65f, 1048575,
+	0xa661, 1048575,
+	0xa663, 1048575,
+	0xa665, 1048575,
+	0xa667, 1048575,
+	0xa669, 1048575,
+	0xa66b, 1048575,
+	0xa66d, 1048575,
+	0xa681, 1048575,
+	0xa683, 1048575,
+	0xa685, 1048575,
+	0xa687, 1048575,
+	0xa689, 1048575,
+	0xa68b, 1048575,
+	0xa68d, 1048575,
+	0xa68f, 1048575,
+	0xa691, 1048575,
+	0xa693, 1048575,
+	0xa695, 1048575,
+	0xa697, 1048575,
+	0xa699, 1048575,
+	0xa69b, 1048575,
+	0xa723, 1048575,
+	0xa725, 1048575,
+	0xa727, 1048575,
+	0xa729, 1048575,
+	0xa72b, 1048575,
+	0xa72d, 1048575,
+	0xa72f, 1048575,
+	0xa733, 1048575,
+	0xa735, 1048575,
+	0xa737, 1048575,
+	0xa739, 1048575,
+	0xa73b, 1048575,
+	0xa73d, 1048575,
+	0xa73f, 1048575,
+	0xa741, 1048575,
+	0xa743, 1048575,
+	0xa745, 1048575,
+	0xa747, 1048575,
+	0xa749, 1048575,
+	0xa74b, 1048575,
+	0xa74d, 1048575,
+	0xa74f, 1048575,
+	0xa751, 1048575,
+	0xa753, 1048575,
+	0xa755, 1048575,
+	0xa757, 1048575,
+	0xa759, 1048575,
+	0xa75b, 1048575,
+	0xa75d, 1048575,
+	0xa75f, 1048575,
+	0xa761, 1048575,
+	0xa763, 1048575,
+	0xa765, 1048575,
+	0xa767, 1048575,
+	0xa769, 1048575,
+	0xa76b, 1048575,
+	0xa76d, 1048575,
+	0xa76f, 1048575,
+	0xa77a, 1048575,
+	0xa77c, 1048575,
+	0xa77f, 1048575,
+	0xa781, 1048575,
+	0xa783, 1048575,
+	0xa785, 1048575,
+	0xa787, 1048575,
+	0xa78c, 1048575,
+	0xa791, 1048575,
+	0xa793, 1048575,
+	0xa797, 1048575,
+	0xa799, 1048575,
+	0xa79b, 1048575,
+	0xa79d, 1048575,
+	0xa79f, 1048575,
+	0xa7a1, 1048575,
+	0xa7a3, 1048575,
+	0xa7a5, 1048575,
+	0xa7a7, 1048575,
+	0xa7a9, 1048575,
+};
+
+char32_t toupper(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, toupperr, nelem (toupperr)/3, 3);
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = rbsearch(c, touppers, nelem (touppers)/2, 2);
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+char32_t tolowerr[] = {
+	0x0041, 0x005a, 1048608,
+	0x00c0, 0x00d6, 1048608,
+	0x00d8, 0x00de, 1048608,
+	0x0189, 0x018a, 1048781,
+	0x01b1, 0x01b2, 1048793,
+	0x0388, 0x038a, 1048613,
+	0x038e, 0x038f, 1048639,
+	0x0391, 0x03a1, 1048608,
+	0x03a3, 0x03ab, 1048608,
+	0x03fd, 0x03ff, 1048446,
+	0x0400, 0x040f, 1048656,
+	0x0410, 0x042f, 1048608,
+	0x0531, 0x0556, 1048624,
+	0x10a0, 0x10c5, 1055840,
+	0x1f08, 0x1f0f, 1048568,
+	0x1f18, 0x1f1d, 1048568,
+	0x1f28, 0x1f2f, 1048568,
+	0x1f38, 0x1f3f, 1048568,
+	0x1f48, 0x1f4d, 1048568,
+	0x1f68, 0x1f6f, 1048568,
+	0x1f88, 0x1f8f, 1048568,
+	0x1f98, 0x1f9f, 1048568,
+	0x1fa8, 0x1faf, 1048568,
+	0x1fb8, 0x1fb9, 1048568,
+	0x1fba, 0x1fbb, 1048502,
+	0x1fc8, 0x1fcb, 1048490,
+	0x1fd8, 0x1fd9, 1048568,
+	0x1fda, 0x1fdb, 1048476,
+	0x1fe8, 0x1fe9, 1048568,
+	0x1fea, 0x1feb, 1048464,
+	0x1ff8, 0x1ff9, 1048448,
+	0x1ffa, 0x1ffb, 1048450,
+	0x2160, 0x216f, 1048592,
+	0x24b6, 0x24cf, 1048602,
+	0x2c00, 0x2c2e, 1048624,
+	0x2c7e, 0x2c7f, 1037761,
+	0xff21, 0xff3a, 1048608,
+	0x10400, 0x10427, 1048616,
+	0x118a0, 0x118bf, 1048608,
+};
+
+static char32_t tolowers[] = {
+	0x0100, 1048577,
+	0x0102, 1048577,
+	0x0104, 1048577,
+	0x0106, 1048577,
+	0x0108, 1048577,
+	0x010a, 1048577,
+	0x010c, 1048577,
+	0x010e, 1048577,
+	0x0110, 1048577,
+	0x0112, 1048577,
+	0x0114, 1048577,
+	0x0116, 1048577,
+	0x0118, 1048577,
+	0x011a, 1048577,
+	0x011c, 1048577,
+	0x011e, 1048577,
+	0x0120, 1048577,
+	0x0122, 1048577,
+	0x0124, 1048577,
+	0x0126, 1048577,
+	0x0128, 1048577,
+	0x012a, 1048577,
+	0x012c, 1048577,
+	0x012e, 1048577,
+	0x0130, 1048377,
+	0x0132, 1048577,
+	0x0134, 1048577,
+	0x0136, 1048577,
+	0x0139, 1048577,
+	0x013b, 1048577,
+	0x013d, 1048577,
+	0x013f, 1048577,
+	0x0141, 1048577,
+	0x0143, 1048577,
+	0x0145, 1048577,
+	0x0147, 1048577,
+	0x014a, 1048577,
+	0x014c, 1048577,
+	0x014e, 1048577,
+	0x0150, 1048577,
+	0x0152, 1048577,
+	0x0154, 1048577,
+	0x0156, 1048577,
+	0x0158, 1048577,
+	0x015a, 1048577,
+	0x015c, 1048577,
+	0x015e, 1048577,
+	0x0160, 1048577,
+	0x0162, 1048577,
+	0x0164, 1048577,
+	0x0166, 1048577,
+	0x0168, 1048577,
+	0x016a, 1048577,
+	0x016c, 1048577,
+	0x016e, 1048577,
+	0x0170, 1048577,
+	0x0172, 1048577,
+	0x0174, 1048577,
+	0x0176, 1048577,
+	0x0178, 1048455,
+	0x0179, 1048577,
+	0x017b, 1048577,
+	0x017d, 1048577,
+	0x0181, 1048786,
+	0x0182, 1048577,
+	0x0184, 1048577,
+	0x0186, 1048782,
+	0x0187, 1048577,
+	0x018b, 1048577,
+	0x018e, 1048655,
+	0x018f, 1048778,
+	0x0190, 1048779,
+	0x0191, 1048577,
+	0x0193, 1048781,
+	0x0194, 1048783,
+	0x0196, 1048787,
+	0x0197, 1048785,
+	0x0198, 1048577,
+	0x019c, 1048787,
+	0x019d, 1048789,
+	0x019f, 1048790,
+	0x01a0, 1048577,
+	0x01a2, 1048577,
+	0x01a4, 1048577,
+	0x01a6, 1048794,
+	0x01a7, 1048577,
+	0x01a9, 1048794,
+	0x01ac, 1048577,
+	0x01ae, 1048794,
+	0x01af, 1048577,
+	0x01b3, 1048577,
+	0x01b5, 1048577,
+	0x01b7, 1048795,
+	0x01b8, 1048577,
+	0x01bc, 1048577,
+	0x01c4, 1048578,
+	0x01c5, 1048577,
+	0x01c7, 1048578,
+	0x01c8, 1048577,
+	0x01ca, 1048578,
+	0x01cb, 1048577,
+	0x01cd, 1048577,
+	0x01cf, 1048577,
+	0x01d1, 1048577,
+	0x01d3, 1048577,
+	0x01d5, 1048577,
+	0x01d7, 1048577,
+	0x01d9, 1048577,
+	0x01db, 1048577,
+	0x01de, 1048577,
+	0x01e0, 1048577,
+	0x01e2, 1048577,
+	0x01e4, 1048577,
+	0x01e6, 1048577,
+	0x01e8, 1048577,
+	0x01ea, 1048577,
+	0x01ec, 1048577,
+	0x01ee, 1048577,
+	0x01f1, 1048578,
+	0x01f2, 1048577,
+	0x01f4, 1048577,
+	0x01f6, 1048479,
+	0x01f7, 1048520,
+	0x01f8, 1048577,
+	0x01fa, 1048577,
+	0x01fc, 1048577,
+	0x01fe, 1048577,
+	0x0200, 1048577,
+	0x0202, 1048577,
+	0x0204, 1048577,
+	0x0206, 1048577,
+	0x0208, 1048577,
+	0x020a, 1048577,
+	0x020c, 1048577,
+	0x020e, 1048577,
+	0x0210, 1048577,
+	0x0212, 1048577,
+	0x0214, 1048577,
+	0x0216, 1048577,
+	0x0218, 1048577,
+	0x021a, 1048577,
+	0x021c, 1048577,
+	0x021e, 1048577,
+	0x0220, 1048446,
+	0x0222, 1048577,
+	0x0224, 1048577,
+	0x0226, 1048577,
+	0x0228, 1048577,
+	0x022a, 1048577,
+	0x022c, 1048577,
+	0x022e, 1048577,
+	0x0230, 1048577,
+	0x0232, 1048577,
+	0x023a, 1059371,
+	0x023b, 1048577,
+	0x023d, 1048413,
+	0x023e, 1059368,
+	0x0241, 1048577,
+	0x0243, 1048381,
+	0x0244, 1048645,
+	0x0245, 1048647,
+	0x0246, 1048577,
+	0x0248, 1048577,
+	0x024a, 1048577,
+	0x024c, 1048577,
+	0x024e, 1048577,
+	0x0370, 1048577,
+	0x0372, 1048577,
+	0x0376, 1048577,
+	0x037f, 1048692,
+	0x0386, 1048614,
+	0x038c, 1048640,
+	0x03cf, 1048584,
+	0x03d8, 1048577,
+	0x03da, 1048577,
+	0x03dc, 1048577,
+	0x03de, 1048577,
+	0x03e0, 1048577,
+	0x03e2, 1048577,
+	0x03e4, 1048577,
+	0x03e6, 1048577,
+	0x03e8, 1048577,
+	0x03ea, 1048577,
+	0x03ec, 1048577,
+	0x03ee, 1048577,
+	0x03f4, 1048516,
+	0x03f7, 1048577,
+	0x03f9, 1048569,
+	0x03fa, 1048577,
+	0x0460, 1048577,
+	0x0462, 1048577,
+	0x0464, 1048577,
+	0x0466, 1048577,
+	0x0468, 1048577,
+	0x046a, 1048577,
+	0x046c, 1048577,
+	0x046e, 1048577,
+	0x0470, 1048577,
+	0x0472, 1048577,
+	0x0474, 1048577,
+	0x0476, 1048577,
+	0x0478, 1048577,
+	0x047a, 1048577,
+	0x047c, 1048577,
+	0x047e, 1048577,
+	0x0480, 1048577,
+	0x048a, 1048577,
+	0x048c, 1048577,
+	0x048e, 1048577,
+	0x0490, 1048577,
+	0x0492, 1048577,
+	0x0494, 1048577,
+	0x0496, 1048577,
+	0x0498, 1048577,
+	0x049a, 1048577,
+	0x049c, 1048577,
+	0x049e, 1048577,
+	0x04a0, 1048577,
+	0x04a2, 1048577,
+	0x04a4, 1048577,
+	0x04a6, 1048577,
+	0x04a8, 1048577,
+	0x04aa, 1048577,
+	0x04ac, 1048577,
+	0x04ae, 1048577,
+	0x04b0, 1048577,
+	0x04b2, 1048577,
+	0x04b4, 1048577,
+	0x04b6, 1048577,
+	0x04b8, 1048577,
+	0x04ba, 1048577,
+	0x04bc, 1048577,
+	0x04be, 1048577,
+	0x04c0, 1048591,
+	0x04c1, 1048577,
+	0x04c3, 1048577,
+	0x04c5, 1048577,
+	0x04c7, 1048577,
+	0x04c9, 1048577,
+	0x04cb, 1048577,
+	0x04cd, 1048577,
+	0x04d0, 1048577,
+	0x04d2, 1048577,
+	0x04d4, 1048577,
+	0x04d6, 1048577,
+	0x04d8, 1048577,
+	0x04da, 1048577,
+	0x04dc, 1048577,
+	0x04de, 1048577,
+	0x04e0, 1048577,
+	0x04e2, 1048577,
+	0x04e4, 1048577,
+	0x04e6, 1048577,
+	0x04e8, 1048577,
+	0x04ea, 1048577,
+	0x04ec, 1048577,
+	0x04ee, 1048577,
+	0x04f0, 1048577,
+	0x04f2, 1048577,
+	0x04f4, 1048577,
+	0x04f6, 1048577,
+	0x04f8, 1048577,
+	0x04fa, 1048577,
+	0x04fc, 1048577,
+	0x04fe, 1048577,
+	0x0500, 1048577,
+	0x0502, 1048577,
+	0x0504, 1048577,
+	0x0506, 1048577,
+	0x0508, 1048577,
+	0x050a, 1048577,
+	0x050c, 1048577,
+	0x050e, 1048577,
+	0x0510, 1048577,
+	0x0512, 1048577,
+	0x0514, 1048577,
+	0x0516, 1048577,
+	0x0518, 1048577,
+	0x051a, 1048577,
+	0x051c, 1048577,
+	0x051e, 1048577,
+	0x0520, 1048577,
+	0x0522, 1048577,
+	0x0524, 1048577,
+	0x0526, 1048577,
+	0x0528, 1048577,
+	0x052a, 1048577,
+	0x052c, 1048577,
+	0x052e, 1048577,
+	0x10c7, 1055840,
+	0x10cd, 1055840,
+	0x1e00, 1048577,
+	0x1e02, 1048577,
+	0x1e04, 1048577,
+	0x1e06, 1048577,
+	0x1e08, 1048577,
+	0x1e0a, 1048577,
+	0x1e0c, 1048577,
+	0x1e0e, 1048577,
+	0x1e10, 1048577,
+	0x1e12, 1048577,
+	0x1e14, 1048577,
+	0x1e16, 1048577,
+	0x1e18, 1048577,
+	0x1e1a, 1048577,
+	0x1e1c, 1048577,
+	0x1e1e, 1048577,
+	0x1e20, 1048577,
+	0x1e22, 1048577,
+	0x1e24, 1048577,
+	0x1e26, 1048577,
+	0x1e28, 1048577,
+	0x1e2a, 1048577,
+	0x1e2c, 1048577,
+	0x1e2e, 1048577,
+	0x1e30, 1048577,
+	0x1e32, 1048577,
+	0x1e34, 1048577,
+	0x1e36, 1048577,
+	0x1e38, 1048577,
+	0x1e3a, 1048577,
+	0x1e3c, 1048577,
+	0x1e3e, 1048577,
+	0x1e40, 1048577,
+	0x1e42, 1048577,
+	0x1e44, 1048577,
+	0x1e46, 1048577,
+	0x1e48, 1048577,
+	0x1e4a, 1048577,
+	0x1e4c, 1048577,
+	0x1e4e, 1048577,
+	0x1e50, 1048577,
+	0x1e52, 1048577,
+	0x1e54, 1048577,
+	0x1e56, 1048577,
+	0x1e58, 1048577,
+	0x1e5a, 1048577,
+	0x1e5c, 1048577,
+	0x1e5e, 1048577,
+	0x1e60, 1048577,
+	0x1e62, 1048577,
+	0x1e64, 1048577,
+	0x1e66, 1048577,
+	0x1e68, 1048577,
+	0x1e6a, 1048577,
+	0x1e6c, 1048577,
+	0x1e6e, 1048577,
+	0x1e70, 1048577,
+	0x1e72, 1048577,
+	0x1e74, 1048577,
+	0x1e76, 1048577,
+	0x1e78, 1048577,
+	0x1e7a, 1048577,
+	0x1e7c, 1048577,
+	0x1e7e, 1048577,
+	0x1e80, 1048577,
+	0x1e82, 1048577,
+	0x1e84, 1048577,
+	0x1e86, 1048577,
+	0x1e88, 1048577,
+	0x1e8a, 1048577,
+	0x1e8c, 1048577,
+	0x1e8e, 1048577,
+	0x1e90, 1048577,
+	0x1e92, 1048577,
+	0x1e94, 1048577,
+	0x1e9e, 1040961,
+	0x1ea0, 1048577,
+	0x1ea2, 1048577,
+	0x1ea4, 1048577,
+	0x1ea6, 1048577,
+	0x1ea8, 1048577,
+	0x1eaa, 1048577,
+	0x1eac, 1048577,
+	0x1eae, 1048577,
+	0x1eb0, 1048577,
+	0x1eb2, 1048577,
+	0x1eb4, 1048577,
+	0x1eb6, 1048577,
+	0x1eb8, 1048577,
+	0x1eba, 1048577,
+	0x1ebc, 1048577,
+	0x1ebe, 1048577,
+	0x1ec0, 1048577,
+	0x1ec2, 1048577,
+	0x1ec4, 1048577,
+	0x1ec6, 1048577,
+	0x1ec8, 1048577,
+	0x1eca, 1048577,
+	0x1ecc, 1048577,
+	0x1ece, 1048577,
+	0x1ed0, 1048577,
+	0x1ed2, 1048577,
+	0x1ed4, 1048577,
+	0x1ed6, 1048577,
+	0x1ed8, 1048577,
+	0x1eda, 1048577,
+	0x1edc, 1048577,
+	0x1ede, 1048577,
+	0x1ee0, 1048577,
+	0x1ee2, 1048577,
+	0x1ee4, 1048577,
+	0x1ee6, 1048577,
+	0x1ee8, 1048577,
+	0x1eea, 1048577,
+	0x1eec, 1048577,
+	0x1eee, 1048577,
+	0x1ef0, 1048577,
+	0x1ef2, 1048577,
+	0x1ef4, 1048577,
+	0x1ef6, 1048577,
+	0x1ef8, 1048577,
+	0x1efa, 1048577,
+	0x1efc, 1048577,
+	0x1efe, 1048577,
+	0x1f59, 1048568,
+	0x1f5b, 1048568,
+	0x1f5d, 1048568,
+	0x1f5f, 1048568,
+	0x1fbc, 1048567,
+	0x1fcc, 1048567,
+	0x1fec, 1048569,
+	0x1ffc, 1048567,
+	0x2126, 1041059,
+	0x212a, 1040193,
+	0x212b, 1040314,
+	0x2132, 1048604,
+	0x2183, 1048577,
+	0x2c60, 1048577,
+	0x2c62, 1037833,
+	0x2c63, 1044762,
+	0x2c64, 1037849,
+	0x2c67, 1048577,
+	0x2c69, 1048577,
+	0x2c6b, 1048577,
+	0x2c6d, 1037796,
+	0x2c6e, 1037827,
+	0x2c6f, 1037793,
+	0x2c70, 1037794,
+	0x2c72, 1048577,
+	0x2c75, 1048577,
+	0x2c80, 1048577,
+	0x2c82, 1048577,
+	0x2c84, 1048577,
+	0x2c86, 1048577,
+	0x2c88, 1048577,
+	0x2c8a, 1048577,
+	0x2c8c, 1048577,
+	0x2c8e, 1048577,
+	0x2c90, 1048577,
+	0x2c92, 1048577,
+	0x2c94, 1048577,
+	0x2c96, 1048577,
+	0x2c98, 1048577,
+	0x2c9a, 1048577,
+	0x2c9c, 1048577,
+	0x2c9e, 1048577,
+	0x2ca0, 1048577,
+	0x2ca2, 1048577,
+	0x2ca4, 1048577,
+	0x2ca6, 1048577,
+	0x2ca8, 1048577,
+	0x2caa, 1048577,
+	0x2cac, 1048577,
+	0x2cae, 1048577,
+	0x2cb0, 1048577,
+	0x2cb2, 1048577,
+	0x2cb4, 1048577,
+	0x2cb6, 1048577,
+	0x2cb8, 1048577,
+	0x2cba, 1048577,
+	0x2cbc, 1048577,
+	0x2cbe, 1048577,
+	0x2cc0, 1048577,
+	0x2cc2, 1048577,
+	0x2cc4, 1048577,
+	0x2cc6, 1048577,
+	0x2cc8, 1048577,
+	0x2cca, 1048577,
+	0x2ccc, 1048577,
+	0x2cce, 1048577,
+	0x2cd0, 1048577,
+	0x2cd2, 1048577,
+	0x2cd4, 1048577,
+	0x2cd6, 1048577,
+	0x2cd8, 1048577,
+	0x2cda, 1048577,
+	0x2cdc, 1048577,
+	0x2cde, 1048577,
+	0x2ce0, 1048577,
+	0x2ce2, 1048577,
+	0x2ceb, 1048577,
+	0x2ced, 1048577,
+	0x2cf2, 1048577,
+	0xa640, 1048577,
+	0xa642, 1048577,
+	0xa644, 1048577,
+	0xa646, 1048577,
+	0xa648, 1048577,
+	0xa64a, 1048577,
+	0xa64c, 1048577,
+	0xa64e, 1048577,
+	0xa650, 1048577,
+	0xa652, 1048577,
+	0xa654, 1048577,
+	0xa656, 1048577,
+	0xa658, 1048577,
+	0xa65a, 1048577,
+	0xa65c, 1048577,
+	0xa65e, 1048577,
+	0xa660, 1048577,
+	0xa662, 1048577,
+	0xa664, 1048577,
+	0xa666, 1048577,
+	0xa668, 1048577,
+	0xa66a, 1048577,
+	0xa66c, 1048577,
+	0xa680, 1048577,
+	0xa682, 1048577,
+	0xa684, 1048577,
+	0xa686, 1048577,
+	0xa688, 1048577,
+	0xa68a, 1048577,
+	0xa68c, 1048577,
+	0xa68e, 1048577,
+	0xa690, 1048577,
+	0xa692, 1048577,
+	0xa694, 1048577,
+	0xa696, 1048577,
+	0xa698, 1048577,
+	0xa69a, 1048577,
+	0xa722, 1048577,
+	0xa724, 1048577,
+	0xa726, 1048577,
+	0xa728, 1048577,
+	0xa72a, 1048577,
+	0xa72c, 1048577,
+	0xa72e, 1048577,
+	0xa732, 1048577,
+	0xa734, 1048577,
+	0xa736, 1048577,
+	0xa738, 1048577,
+	0xa73a, 1048577,
+	0xa73c, 1048577,
+	0xa73e, 1048577,
+	0xa740, 1048577,
+	0xa742, 1048577,
+	0xa744, 1048577,
+	0xa746, 1048577,
+	0xa748, 1048577,
+	0xa74a, 1048577,
+	0xa74c, 1048577,
+	0xa74e, 1048577,
+	0xa750, 1048577,
+	0xa752, 1048577,
+	0xa754, 1048577,
+	0xa756, 1048577,
+	0xa758, 1048577,
+	0xa75a, 1048577,
+	0xa75c, 1048577,
+	0xa75e, 1048577,
+	0xa760, 1048577,
+	0xa762, 1048577,
+	0xa764, 1048577,
+	0xa766, 1048577,
+	0xa768, 1048577,
+	0xa76a, 1048577,
+	0xa76c, 1048577,
+	0xa76e, 1048577,
+	0xa779, 1048577,
+	0xa77b, 1048577,
+	0xa77d, 1013244,
+	0xa77e, 1048577,
+	0xa780, 1048577,
+	0xa782, 1048577,
+	0xa784, 1048577,
+	0xa786, 1048577,
+	0xa78b, 1048577,
+	0xa78d, 1006296,
+	0xa790, 1048577,
+	0xa792, 1048577,
+	0xa796, 1048577,
+	0xa798, 1048577,
+	0xa79a, 1048577,
+	0xa79c, 1048577,
+	0xa79e, 1048577,
+	0xa7a0, 1048577,
+	0xa7a2, 1048577,
+	0xa7a4, 1048577,
+	0xa7a6, 1048577,
+	0xa7a8, 1048577,
+	0xa7aa, 1006268,
+	0xa7ab, 1006257,
+	0xa7ac, 1006261,
+	0xa7ad, 1006271,
+	0xa7b0, 1006318,
+	0xa7b1, 1006294,
+};
+
+char32_t tolower(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, tolowerr, nelem (tolowerr)/3, 3);
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = rbsearch(c, tolowers, nelem (tolowers)/2, 2);
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+char32_t totitler[] = {
+	0x0061, 0x007a, 1048544,
+	0x00e0, 0x00f6, 1048544,
+	0x00f8, 0x00fe, 1048544,
+	0x023f, 0x0240, 1059391,
+	0x0256, 0x0257, 1048371,
+	0x028a, 0x028b, 1048359,
+	0x037b, 0x037d, 1048706,
+	0x03ad, 0x03af, 1048539,
+	0x03b1, 0x03c1, 1048544,
+	0x03c3, 0x03cb, 1048544,
+	0x03cd, 0x03ce, 1048513,
+	0x0430, 0x044f, 1048544,
+	0x0450, 0x045f, 1048496,
+	0x0561, 0x0586, 1048528,
+	0x1f00, 0x1f07, 1048584,
+	0x1f10, 0x1f15, 1048584,
+	0x1f20, 0x1f27, 1048584,
+	0x1f30, 0x1f37, 1048584,
+	0x1f40, 0x1f45, 1048584,
+	0x1f60, 0x1f67, 1048584,
+	0x1f70, 0x1f71, 1048650,
+	0x1f72, 0x1f75, 1048662,
+	0x1f76, 0x1f77, 1048676,
+	0x1f78, 0x1f79, 1048704,
+	0x1f7a, 0x1f7b, 1048688,
+	0x1f7c, 0x1f7d, 1048702,
+	0x1f80, 0x1f87, 1048584,
+	0x1f90, 0x1f97, 1048584,
+	0x1fa0, 0x1fa7, 1048584,
+	0x1fb0, 0x1fb1, 1048584,
+	0x1fd0, 0x1fd1, 1048584,
+	0x1fe0, 0x1fe1, 1048584,
+	0x2170, 0x217f, 1048560,
+	0x24d0, 0x24e9, 1048550,
+	0x2c30, 0x2c5e, 1048528,
+	0x2d00, 0x2d25, 1041312,
+	0xff41, 0xff5a, 1048544,
+	0x10428, 0x1044f, 1048536,
+	0x118c0, 0x118df, 1048544,
+};
+
+static char32_t totitles[] = {
+	0x00b5, 1049319,
+	0x00ff, 1048697,
+	0x0101, 1048575,
+	0x0103, 1048575,
+	0x0105, 1048575,
+	0x0107, 1048575,
+	0x0109, 1048575,
+	0x010b, 1048575,
+	0x010d, 1048575,
+	0x010f, 1048575,
+	0x0111, 1048575,
+	0x0113, 1048575,
+	0x0115, 1048575,
+	0x0117, 1048575,
+	0x0119, 1048575,
+	0x011b, 1048575,
+	0x011d, 1048575,
+	0x011f, 1048575,
+	0x0121, 1048575,
+	0x0123, 1048575,
+	0x0125, 1048575,
+	0x0127, 1048575,
+	0x0129, 1048575,
+	0x012b, 1048575,
+	0x012d, 1048575,
+	0x012f, 1048575,
+	0x0131, 1048344,
+	0x0133, 1048575,
+	0x0135, 1048575,
+	0x0137, 1048575,
+	0x013a, 1048575,
+	0x013c, 1048575,
+	0x013e, 1048575,
+	0x0140, 1048575,
+	0x0142, 1048575,
+	0x0144, 1048575,
+	0x0146, 1048575,
+	0x0148, 1048575,
+	0x014b, 1048575,
+	0x014d, 1048575,
+	0x014f, 1048575,
+	0x0151, 1048575,
+	0x0153, 1048575,
+	0x0155, 1048575,
+	0x0157, 1048575,
+	0x0159, 1048575,
+	0x015b, 1048575,
+	0x015d, 1048575,
+	0x015f, 1048575,
+	0x0161, 1048575,
+	0x0163, 1048575,
+	0x0165, 1048575,
+	0x0167, 1048575,
+	0x0169, 1048575,
+	0x016b, 1048575,
+	0x016d, 1048575,
+	0x016f, 1048575,
+	0x0171, 1048575,
+	0x0173, 1048575,
+	0x0175, 1048575,
+	0x0177, 1048575,
+	0x017a, 1048575,
+	0x017c, 1048575,
+	0x017e, 1048575,
+	0x017f, 1048276,
+	0x0180, 1048771,
+	0x0183, 1048575,
+	0x0185, 1048575,
+	0x0188, 1048575,
+	0x018c, 1048575,
+	0x0192, 1048575,
+	0x0195, 1048673,
+	0x0199, 1048575,
+	0x019a, 1048739,
+	0x019e, 1048706,
+	0x01a1, 1048575,
+	0x01a3, 1048575,
+	0x01a5, 1048575,
+	0x01a8, 1048575,
+	0x01ad, 1048575,
+	0x01b0, 1048575,
+	0x01b4, 1048575,
+	0x01b6, 1048575,
+	0x01b9, 1048575,
+	0x01bd, 1048575,
+	0x01bf, 1048632,
+	0x01c4, 1048577,
+	0x01c6, 1048575,
+	0x01c7, 1048577,
+	0x01c9, 1048575,
+	0x01ca, 1048577,
+	0x01cc, 1048575,
+	0x01ce, 1048575,
+	0x01d0, 1048575,
+	0x01d2, 1048575,
+	0x01d4, 1048575,
+	0x01d6, 1048575,
+	0x01d8, 1048575,
+	0x01da, 1048575,
+	0x01dc, 1048575,
+	0x01dd, 1048497,
+	0x01df, 1048575,
+	0x01e1, 1048575,
+	0x01e3, 1048575,
+	0x01e5, 1048575,
+	0x01e7, 1048575,
+	0x01e9, 1048575,
+	0x01eb, 1048575,
+	0x01ed, 1048575,
+	0x01ef, 1048575,
+	0x01f1, 1048577,
+	0x01f3, 1048575,
+	0x01f5, 1048575,
+	0x01f9, 1048575,
+	0x01fb, 1048575,
+	0x01fd, 1048575,
+	0x01ff, 1048575,
+	0x0201, 1048575,
+	0x0203, 1048575,
+	0x0205, 1048575,
+	0x0207, 1048575,
+	0x0209, 1048575,
+	0x020b, 1048575,
+	0x020d, 1048575,
+	0x020f, 1048575,
+	0x0211, 1048575,
+	0x0213, 1048575,
+	0x0215, 1048575,
+	0x0217, 1048575,
+	0x0219, 1048575,
+	0x021b, 1048575,
+	0x021d, 1048575,
+	0x021f, 1048575,
+	0x0223, 1048575,
+	0x0225, 1048575,
+	0x0227, 1048575,
+	0x0229, 1048575,
+	0x022b, 1048575,
+	0x022d, 1048575,
+	0x022f, 1048575,
+	0x0231, 1048575,
+	0x0233, 1048575,
+	0x023c, 1048575,
+	0x0242, 1048575,
+	0x0247, 1048575,
+	0x0249, 1048575,
+	0x024b, 1048575,
+	0x024d, 1048575,
+	0x024f, 1048575,
+	0x0250, 1059359,
+	0x0251, 1059356,
+	0x0252, 1059358,
+	0x0253, 1048366,
+	0x0254, 1048370,
+	0x0259, 1048374,
+	0x025b, 1048373,
+	0x025c, 1090895,
+	0x0260, 1048371,
+	0x0261, 1090891,
+	0x0263, 1048369,
+	0x0265, 1090856,
+	0x0266, 1090884,
+	0x0268, 1048367,
+	0x0269, 1048365,
+	0x026b, 1059319,
+	0x026c, 1090881,
+	0x026f, 1048365,
+	0x0271, 1059325,
+	0x0272, 1048363,
+	0x0275, 1048362,
+	0x027d, 1059303,
+	0x0280, 1048358,
+	0x0283, 1048358,
+	0x0287, 1090858,
+	0x0288, 1048358,
+	0x0289, 1048507,
+	0x028c, 1048505,
+	0x0292, 1048357,
+	0x029e, 1090834,
+	0x0345, 1048660,
+	0x0371, 1048575,
+	0x0373, 1048575,
+	0x0377, 1048575,
+	0x03ac, 1048538,
+	0x03c2, 1048545,
+	0x03cc, 1048512,
+	0x03d0, 1048514,
+	0x03d1, 1048519,
+	0x03d5, 1048529,
+	0x03d6, 1048522,
+	0x03d7, 1048568,
+	0x03d9, 1048575,
+	0x03db, 1048575,
+	0x03dd, 1048575,
+	0x03df, 1048575,
+	0x03e1, 1048575,
+	0x03e3, 1048575,
+	0x03e5, 1048575,
+	0x03e7, 1048575,
+	0x03e9, 1048575,
+	0x03eb, 1048575,
+	0x03ed, 1048575,
+	0x03ef, 1048575,
+	0x03f0, 1048490,
+	0x03f1, 1048496,
+	0x03f2, 1048583,
+	0x03f3, 1048460,
+	0x03f5, 1048480,
+	0x03f8, 1048575,
+	0x03fb, 1048575,
+	0x0461, 1048575,
+	0x0463, 1048575,
+	0x0465, 1048575,
+	0x0467, 1048575,
+	0x0469, 1048575,
+	0x046b, 1048575,
+	0x046d, 1048575,
+	0x046f, 1048575,
+	0x0471, 1048575,
+	0x0473, 1048575,
+	0x0475, 1048575,
+	0x0477, 1048575,
+	0x0479, 1048575,
+	0x047b, 1048575,
+	0x047d, 1048575,
+	0x047f, 1048575,
+	0x0481, 1048575,
+	0x048b, 1048575,
+	0x048d, 1048575,
+	0x048f, 1048575,
+	0x0491, 1048575,
+	0x0493, 1048575,
+	0x0495, 1048575,
+	0x0497, 1048575,
+	0x0499, 1048575,
+	0x049b, 1048575,
+	0x049d, 1048575,
+	0x049f, 1048575,
+	0x04a1, 1048575,
+	0x04a3, 1048575,
+	0x04a5, 1048575,
+	0x04a7, 1048575,
+	0x04a9, 1048575,
+	0x04ab, 1048575,
+	0x04ad, 1048575,
+	0x04af, 1048575,
+	0x04b1, 1048575,
+	0x04b3, 1048575,
+	0x04b5, 1048575,
+	0x04b7, 1048575,
+	0x04b9, 1048575,
+	0x04bb, 1048575,
+	0x04bd, 1048575,
+	0x04bf, 1048575,
+	0x04c2, 1048575,
+	0x04c4, 1048575,
+	0x04c6, 1048575,
+	0x04c8, 1048575,
+	0x04ca, 1048575,
+	0x04cc, 1048575,
+	0x04ce, 1048575,
+	0x04cf, 1048561,
+	0x04d1, 1048575,
+	0x04d3, 1048575,
+	0x04d5, 1048575,
+	0x04d7, 1048575,
+	0x04d9, 1048575,
+	0x04db, 1048575,
+	0x04dd, 1048575,
+	0x04df, 1048575,
+	0x04e1, 1048575,
+	0x04e3, 1048575,
+	0x04e5, 1048575,
+	0x04e7, 1048575,
+	0x04e9, 1048575,
+	0x04eb, 1048575,
+	0x04ed, 1048575,
+	0x04ef, 1048575,
+	0x04f1, 1048575,
+	0x04f3, 1048575,
+	0x04f5, 1048575,
+	0x04f7, 1048575,
+	0x04f9, 1048575,
+	0x04fb, 1048575,
+	0x04fd, 1048575,
+	0x04ff, 1048575,
+	0x0501, 1048575,
+	0x0503, 1048575,
+	0x0505, 1048575,
+	0x0507, 1048575,
+	0x0509, 1048575,
+	0x050b, 1048575,
+	0x050d, 1048575,
+	0x050f, 1048575,
+	0x0511, 1048575,
+	0x0513, 1048575,
+	0x0515, 1048575,
+	0x0517, 1048575,
+	0x0519, 1048575,
+	0x051b, 1048575,
+	0x051d, 1048575,
+	0x051f, 1048575,
+	0x0521, 1048575,
+	0x0523, 1048575,
+	0x0525, 1048575,
+	0x0527, 1048575,
+	0x0529, 1048575,
+	0x052b, 1048575,
+	0x052d, 1048575,
+	0x052f, 1048575,
+	0x1d79, 1083908,
+	0x1d7d, 1052390,
+	0x1e01, 1048575,
+	0x1e03, 1048575,
+	0x1e05, 1048575,
+	0x1e07, 1048575,
+	0x1e09, 1048575,
+	0x1e0b, 1048575,
+	0x1e0d, 1048575,
+	0x1e0f, 1048575,
+	0x1e11, 1048575,
+	0x1e13, 1048575,
+	0x1e15, 1048575,
+	0x1e17, 1048575,
+	0x1e19, 1048575,
+	0x1e1b, 1048575,
+	0x1e1d, 1048575,
+	0x1e1f, 1048575,
+	0x1e21, 1048575,
+	0x1e23, 1048575,
+	0x1e25, 1048575,
+	0x1e27, 1048575,
+	0x1e29, 1048575,
+	0x1e2b, 1048575,
+	0x1e2d, 1048575,
+	0x1e2f, 1048575,
+	0x1e31, 1048575,
+	0x1e33, 1048575,
+	0x1e35, 1048575,
+	0x1e37, 1048575,
+	0x1e39, 1048575,
+	0x1e3b, 1048575,
+	0x1e3d, 1048575,
+	0x1e3f, 1048575,
+	0x1e41, 1048575,
+	0x1e43, 1048575,
+	0x1e45, 1048575,
+	0x1e47, 1048575,
+	0x1e49, 1048575,
+	0x1e4b, 1048575,
+	0x1e4d, 1048575,
+	0x1e4f, 1048575,
+	0x1e51, 1048575,
+	0x1e53, 1048575,
+	0x1e55, 1048575,
+	0x1e57, 1048575,
+	0x1e59, 1048575,
+	0x1e5b, 1048575,
+	0x1e5d, 1048575,
+	0x1e5f, 1048575,
+	0x1e61, 1048575,
+	0x1e63, 1048575,
+	0x1e65, 1048575,
+	0x1e67, 1048575,
+	0x1e69, 1048575,
+	0x1e6b, 1048575,
+	0x1e6d, 1048575,
+	0x1e6f, 1048575,
+	0x1e71, 1048575,
+	0x1e73, 1048575,
+	0x1e75, 1048575,
+	0x1e77, 1048575,
+	0x1e79, 1048575,
+	0x1e7b, 1048575,
+	0x1e7d, 1048575,
+	0x1e7f, 1048575,
+	0x1e81, 1048575,
+	0x1e83, 1048575,
+	0x1e85, 1048575,
+	0x1e87, 1048575,
+	0x1e89, 1048575,
+	0x1e8b, 1048575,
+	0x1e8d, 1048575,
+	0x1e8f, 1048575,
+	0x1e91, 1048575,
+	0x1e93, 1048575,
+	0x1e95, 1048575,
+	0x1e9b, 1048517,
+	0x1ea1, 1048575,
+	0x1ea3, 1048575,
+	0x1ea5, 1048575,
+	0x1ea7, 1048575,
+	0x1ea9, 1048575,
+	0x1eab, 1048575,
+	0x1ead, 1048575,
+	0x1eaf, 1048575,
+	0x1eb1, 1048575,
+	0x1eb3, 1048575,
+	0x1eb5, 1048575,
+	0x1eb7, 1048575,
+	0x1eb9, 1048575,
+	0x1ebb, 1048575,
+	0x1ebd, 1048575,
+	0x1ebf, 1048575,
+	0x1ec1, 1048575,
+	0x1ec3, 1048575,
+	0x1ec5, 1048575,
+	0x1ec7, 1048575,
+	0x1ec9, 1048575,
+	0x1ecb, 1048575,
+	0x1ecd, 1048575,
+	0x1ecf, 1048575,
+	0x1ed1, 1048575,
+	0x1ed3, 1048575,
+	0x1ed5, 1048575,
+	0x1ed7, 1048575,
+	0x1ed9, 1048575,
+	0x1edb, 1048575,
+	0x1edd, 1048575,
+	0x1edf, 1048575,
+	0x1ee1, 1048575,
+	0x1ee3, 1048575,
+	0x1ee5, 1048575,
+	0x1ee7, 1048575,
+	0x1ee9, 1048575,
+	0x1eeb, 1048575,
+	0x1eed, 1048575,
+	0x1eef, 1048575,
+	0x1ef1, 1048575,
+	0x1ef3, 1048575,
+	0x1ef5, 1048575,
+	0x1ef7, 1048575,
+	0x1ef9, 1048575,
+	0x1efb, 1048575,
+	0x1efd, 1048575,
+	0x1eff, 1048575,
+	0x1f51, 1048584,
+	0x1f53, 1048584,
+	0x1f55, 1048584,
+	0x1f57, 1048584,
+	0x1fb3, 1048585,
+	0x1fbe, 1041371,
+	0x1fc3, 1048585,
+	0x1fe5, 1048583,
+	0x1ff3, 1048585,
+	0x214e, 1048548,
+	0x2184, 1048575,
+	0x2c61, 1048575,
+	0x2c65, 1037781,
+	0x2c66, 1037784,
+	0x2c68, 1048575,
+	0x2c6a, 1048575,
+	0x2c6c, 1048575,
+	0x2c73, 1048575,
+	0x2c76, 1048575,
+	0x2c81, 1048575,
+	0x2c83, 1048575,
+	0x2c85, 1048575,
+	0x2c87, 1048575,
+	0x2c89, 1048575,
+	0x2c8b, 1048575,
+	0x2c8d, 1048575,
+	0x2c8f, 1048575,
+	0x2c91, 1048575,
+	0x2c93, 1048575,
+	0x2c95, 1048575,
+	0x2c97, 1048575,
+	0x2c99, 1048575,
+	0x2c9b, 1048575,
+	0x2c9d, 1048575,
+	0x2c9f, 1048575,
+	0x2ca1, 1048575,
+	0x2ca3, 1048575,
+	0x2ca5, 1048575,
+	0x2ca7, 1048575,
+	0x2ca9, 1048575,
+	0x2cab, 1048575,
+	0x2cad, 1048575,
+	0x2caf, 1048575,
+	0x2cb1, 1048575,
+	0x2cb3, 1048575,
+	0x2cb5, 1048575,
+	0x2cb7, 1048575,
+	0x2cb9, 1048575,
+	0x2cbb, 1048575,
+	0x2cbd, 1048575,
+	0x2cbf, 1048575,
+	0x2cc1, 1048575,
+	0x2cc3, 1048575,
+	0x2cc5, 1048575,
+	0x2cc7, 1048575,
+	0x2cc9, 1048575,
+	0x2ccb, 1048575,
+	0x2ccd, 1048575,
+	0x2ccf, 1048575,
+	0x2cd1, 1048575,
+	0x2cd3, 1048575,
+	0x2cd5, 1048575,
+	0x2cd7, 1048575,
+	0x2cd9, 1048575,
+	0x2cdb, 1048575,
+	0x2cdd, 1048575,
+	0x2cdf, 1048575,
+	0x2ce1, 1048575,
+	0x2ce3, 1048575,
+	0x2cec, 1048575,
+	0x2cee, 1048575,
+	0x2cf3, 1048575,
+	0x2d27, 1041312,
+	0x2d2d, 1041312,
+	0xa641, 1048575,
+	0xa643, 1048575,
+	0xa645, 1048575,
+	0xa647, 1048575,
+	0xa649, 1048575,
+	0xa64b, 1048575,
+	0xa64d, 1048575,
+	0xa64f, 1048575,
+	0xa651, 1048575,
+	0xa653, 1048575,
+	0xa655, 1048575,
+	0xa657, 1048575,
+	0xa659, 1048575,
+	0xa65b, 1048575,
+	0xa65d, 1048575,
+	0xa65f, 1048575,
+	0xa661, 1048575,
+	0xa663, 1048575,
+	0xa665, 1048575,
+	0xa667, 1048575,
+	0xa669, 1048575,
+	0xa66b, 1048575,
+	0xa66d, 1048575,
+	0xa681, 1048575,
+	0xa683, 1048575,
+	0xa685, 1048575,
+	0xa687, 1048575,
+	0xa689, 1048575,
+	0xa68b, 1048575,
+	0xa68d, 1048575,
+	0xa68f, 1048575,
+	0xa691, 1048575,
+	0xa693, 1048575,
+	0xa695, 1048575,
+	0xa697, 1048575,
+	0xa699, 1048575,
+	0xa69b, 1048575,
+	0xa723, 1048575,
+	0xa725, 1048575,
+	0xa727, 1048575,
+	0xa729, 1048575,
+	0xa72b, 1048575,
+	0xa72d, 1048575,
+	0xa72f, 1048575,
+	0xa733, 1048575,
+	0xa735, 1048575,
+	0xa737, 1048575,
+	0xa739, 1048575,
+	0xa73b, 1048575,
+	0xa73d, 1048575,
+	0xa73f, 1048575,
+	0xa741, 1048575,
+	0xa743, 1048575,
+	0xa745, 1048575,
+	0xa747, 1048575,
+	0xa749, 1048575,
+	0xa74b, 1048575,
+	0xa74d, 1048575,
+	0xa74f, 1048575,
+	0xa751, 1048575,
+	0xa753, 1048575,
+	0xa755, 1048575,
+	0xa757, 1048575,
+	0xa759, 1048575,
+	0xa75b, 1048575,
+	0xa75d, 1048575,
+	0xa75f, 1048575,
+	0xa761, 1048575,
+	0xa763, 1048575,
+	0xa765, 1048575,
+	0xa767, 1048575,
+	0xa769, 1048575,
+	0xa76b, 1048575,
+	0xa76d, 1048575,
+	0xa76f, 1048575,
+	0xa77a, 1048575,
+	0xa77c, 1048575,
+	0xa77f, 1048575,
+	0xa781, 1048575,
+	0xa783, 1048575,
+	0xa785, 1048575,
+	0xa787, 1048575,
+	0xa78c, 1048575,
+	0xa791, 1048575,
+	0xa793, 1048575,
+	0xa797, 1048575,
+	0xa799, 1048575,
+	0xa79b, 1048575,
+	0xa79d, 1048575,
+	0xa79f, 1048575,
+	0xa7a1, 1048575,
+	0xa7a3, 1048575,
+	0xa7a5, 1048575,
+	0xa7a7, 1048575,
+	0xa7a9, 1048575,
+};
+
+char32_t totitle(char32_t c) noexcept
+{
+	char32_t *p;
+
+	p = rbsearch(c, totitler, nelem (totitler)/3, 3);
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = rbsearch(c, totitles, nelem (totitles)/2, 2);
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+void encode(char32_t c, char res[5]) noexcept
+{
+	switch (nbytesPoint(c)) {
+	case 1:
+		res[0] = c;
+		res[1] = '\0';
+		break;
+	case 2:
+		res[0] = 0xC0 | ((c >> 6)  & 0x1F);
+		res[1] = 0x80 | (c & 0x3F);
+		res[2] = '\0';
+		break;
+	case 3:
+		res[0] = 0xE0 | ((c >> 12) & 0xF );
+		res[1] = 0x80 | ((c >> 6)  & 0x3F);
+		res[2] = 0x80 | (c & 0x3F);
+		res[3] = '\0';
+		break;
+	case 4:
+		res[0] = 0xF0 | ((c >> 18) & 0x7 );
+		res[1] = 0x80 | ((c >> 12) & 0x3F);
+		res[2] = 0x80 | ((c >> 6)  & 0x3F);
+		res[3] = 0x80 | (c & 0x3F);
+		res[4] = '\0';
+		break;
+	default:
+		break;
+	}
+}
+
+void decode(char32_t &c, const char *res) noexcept
+{
+	c = 0;
+
+	switch (nbytesUtf8(res[0])) {
+	case 1:
+		c = res[0];
+		break;
+	case 2:
+		c =  (res[0] & 0x1f) << 6;
+		c |= (res[1] & 0x3f);
+		break;
+	case 3:
+		c =  (res[0] & 0x0f) << 12;
+		c |= (res[1] & 0x3f) << 6;
+		c |= (res[2] & 0x3f);
+		break;
+	case 4:
+		c =  (res[0] & 0x07) << 16;
+		c |= (res[1] & 0x3f) << 12;
+		c |= (res[2] & 0x3f) << 6;
+		c |= (res[3] & 0x3f);
+	default:
+		break;
+	}
+}
+
+int nbytesUtf8(char c) noexcept
+{
+	if ((c & 0xE0) == 0xC0)
+		return 2;
+	if ((c & 0xF0) == 0xE0)
+		return 3;
+	if ((c & 0xF8) == 0xF0)
+		return 4;
+
+	return 1;
+}
+
+int nbytesPoint(char32_t c) noexcept
+{
+	if (c <= 0x7F)
+		return 1;
+	if (c <= 0x7FF)
+		return 2;
+	if (c <= 0xFFFF)
+		return 3;
+	if (c <= 0x1FFFFF)
+		return 4;
+
+	return -1;
+}
+
+int length(const std::string &str)
+{
+	int total = 0;
+
+	forEach(str, [&] (char32_t) {
+		++ total;
+	});
+
+	return total;
+}
+
+std::string toUtf8(const std::u32string &array)
+{
+	std::string res;
+
+	for (size_t i = 0; i < array.size(); ++i) {
+		char tmp[5];
+		int size = nbytesPoint(array[i]);
+
+		if (size < 0) {
+			throw std::invalid_argument("invalid sequence");
+		}
+
+		encode(array[i], tmp);
+		res.insert(res.length(), tmp);
+	}
+
+	return res;
+}
+
+std::u32string toUtf32(const std::string &str)
+{
+	std::u32string res;
+
+	forEach(str, [&] (char32_t code) {
+		res.push_back(code);
+	});
+
+	return res;
+}
+
+} // !unicode
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/unicode.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,241 @@
+/*
+ * unicode.h -- UTF-8 to UTF-32 conversions and various operations
+ *
+ * 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_UNICODE_H_
+#define _IRCCD_UNICODE_H_
+
+/**
+ * @file Unicode.h
+ * @brief UTF-8 to UTF-32 conversions
+ */
+
+#include <stdexcept>
+#include <string>
+
+namespace irccd {
+
+namespace unicode {
+
+void encode(char32_t point, char res[5]) noexcept;
+void decode(char32_t &c, const char *res) noexcept;
+
+/**
+ * Get the number of bytes for the first multi byte character from a
+ * utf-8 string.
+ *
+ * This can be used to iterate a valid UTF-8 string to jump to the next
+ * real character.
+ *
+ * @param c the first multi byte character
+ * @return the number of bytes [1-4]
+ */
+int nbytesUtf8(char c) noexcept;
+
+/**
+ * Get the number of bytes for the unicode point.
+ *
+ * @param point the unicode point
+ * @return the number of bytes [1-4] or -1 on invalid
+ */
+int nbytesPoint(char32_t point) noexcept;
+
+/**
+ * Get real number of character in a string.
+ *
+ * @param str the string
+ * @return the length
+ * @throw std::invalid_argument on invalid sequence
+ */
+int length(const std::string &str);
+
+/**
+ * Iterate over all real characters in the UTF-8 string.
+ *
+ * The function must have the following signature:
+ *	void f(char ch)
+ *
+ * @param str the UTF-8 string
+ * @throw std::invalid_argument on invalid sequence
+ */
+template <typename Func>
+void forEach(const std::string &str, Func function)
+{
+	for (size_t i = 0; i < str.size(); ) {
+		char32_t point = 0;
+		int size = nbytesUtf8(str[i]);
+
+		if (size < 0) {
+			throw std::invalid_argument("invalid sequence");
+		}
+
+		decode(point, str.data() + i);
+		function(point);
+
+		i += size;
+	}
+}
+
+/**
+ * Convert a UTF-32 string to UTF-8 string.
+ *
+ * @param array the UTF-32 string
+ * @return the UTF-8 string
+ * @throw std::invalid_argument on invalid sequence
+ */
+std::string toUtf8(const std::u32string &array);
+
+/**
+ * Convert a UTF-8 string to UTF-32 string.
+ *
+ * @param str the UTF-8 string
+ * @return the UTF-32 string
+ * @throw std::invalid_argument on invalid sequence
+ */
+std::u32string toUtf32(const std::string &str);
+
+/**
+ * Check if the unicode character is space.
+ *
+ * @param c the character
+ * @return true if space
+ */
+bool isspace(char32_t c) noexcept;
+
+/**
+ * Check if the unicode character is digit.
+ *
+ * @param c the character
+ * @return true if digit
+ */
+bool isdigit(char32_t c) noexcept;
+
+/**
+ * Check if the unicode character is alpha category.
+ *
+ * @param c the character
+ * @return true if alpha
+ */
+bool isalpha(char32_t c) noexcept;
+
+/**
+ * Check if the unicode character is upper case.
+ *
+ * @param c the character
+ * @return true if upper case
+ */
+bool isupper(char32_t c) noexcept;
+
+/**
+ * Check if the unicode character is lower case.
+ *
+ * @param c the character
+ * @return true if lower case
+ */
+bool islower(char32_t c) noexcept;
+
+/**
+ * Check if the unicode character is title case.
+ *
+ * @param c the character
+ * @return true if title case
+ */
+bool istitle(char32_t c) noexcept;
+
+/**
+ * Convert to upper case.
+ *
+ * @param c the character
+ * @return the upper case character
+ */
+char32_t toupper(char32_t c) noexcept;
+
+/**
+ * Convert to lower case.
+ *
+ * @param c the character
+ * @return the lower case character
+ */
+char32_t tolower(char32_t c) noexcept;
+
+/**
+ * Convert to title case.
+ *
+ * @param c the character
+ * @return the title case character
+ */
+char32_t totitle(char32_t c) noexcept;
+
+/**
+ * Convert the UTF-32 string to upper case.
+ *
+ * @param str the str
+ * @return the upper case string
+ */
+inline std::u32string toupper(std::u32string str)
+{
+	for (size_t i = 0; i < str.size(); ++i) {
+		str[i] = toupper(str[i]);
+	}
+
+	return str;
+}
+
+/**
+ * Convert the UTF-8 string to upper case.
+ *
+ * @param str the str
+ * @return the upper case string
+ * @warning very slow at the moment
+ */
+inline std::string toupper(const std::string &str)
+{
+	return toUtf8(toupper(toUtf32(str)));
+}
+
+/**
+ * Convert the UTF-32 string to lower case.
+ *
+ * @param str the str
+ * @return the lower case string
+ */
+inline std::u32string tolower(std::u32string str)
+{
+	for (size_t i = 0; i < str.size(); ++i) {
+		str[i] = tolower(str[i]);
+	}
+
+	return str;
+}
+
+/**
+ * Convert the UTF-8 string to lower case.
+ *
+ * @param str the str
+ * @return the lower case string
+ * @warning very slow at the moment
+ */
+inline std::string tolower(const std::string &str)
+{
+	return toUtf8(tolower(toUtf32(str)));
+}
+
+} // !unicode
+
+} // !irccd
+
+#endif // !_UTF8_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/util.cpp	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,357 @@
+/*
+ * util.cpp -- some utilities
+ *
+ * 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 <cctype>
+#include <cstdlib>
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <stdexcept>
+
+#include <irccd-config.h>
+
+#include "util.h"
+#include "unicode.h"
+
+using namespace std::string_literals;
+
+namespace irccd {
+
+namespace util {
+
+namespace {
+
+const std::unordered_map<std::string, int> colorTable{
+	{ "white",	0	},
+	{ "black",	1	},
+	{ "blue",	2	},
+	{ "green",	3	},
+	{ "red",	4	},
+	{ "brown",	5	},
+	{ "purple",	6	},
+	{ "orange",	7	},
+	{ "yellow",	8	},
+	{ "lightgreen",	9	},
+	{ "cyan",	10	},
+	{ "lightcyan",	11	},
+	{ "lightblue",	12	},
+	{ "pink",	13	},
+	{ "grey",	14	},
+	{ "lightgrey",	15	}
+};
+
+const std::unordered_map<std::string, char> attributesTable{
+	{ "bold",	'\x02'	},
+	{ "italic",	'\x09'	},
+	{ "strike",	'\x13'	},
+	{ "reset",	'\x0f'	},
+	{ "underline",	'\x15'	},
+	{ "underline2",	'\x1f'	},
+	{ "reverse",	'\x16'	}
+};
+
+std::string substituteDate(const std::string &text, const Substitution &params)
+{
+	std::ostringstream oss;
+
+#if defined(HAVE_STD_PUT_TIME)
+	oss << std::put_time(std::localtime(&params.time), text.c_str());
+#else
+	/*
+	 * Quick and dirty hack because GCC does not have this function.
+	 */
+	char buffer[4096];
+
+	std::strftime(buffer, sizeof (buffer) - 1, text.c_str(), std::localtime(&params.time));
+
+	oss << buffer;
+#endif
+
+	return oss.str();
+}
+
+std::string substituteKeywords(const std::string &content, const Substitution &params)
+{
+	auto value = params.keywords.find(content);
+
+	if (value != params.keywords.end())
+		return value->second;
+
+	return "";
+}
+
+std::string substituteEnv(const std::string &content)
+{
+	auto value = std::getenv(content.c_str());
+
+	if (value != nullptr)
+		return value;
+
+	return "";
+}
+
+std::string substituteAttributes(const std::string &content)
+{
+	std::stringstream oss;
+	std::vector<std::string> list = split(content, ",");
+
+	/* @{} means reset */
+	if (list.empty()) {
+		oss << attributesTable.at("reset");
+	} else {
+		/* Remove useless spaces */
+		for (auto &a : list)
+			a = strip(a);
+
+		/*
+		 * 0: foreground
+		 * 1: background
+		 * 2-n: attributes
+		 */
+		auto foreground = list[0];
+		if (!foreground.empty() || list.size() >= 2) {
+			/* Color sequence */
+			oss << '\x03';
+
+			/* Foreground */
+			auto it = colorTable.find(foreground);
+			if (it != colorTable.end())
+				oss << it->second;
+
+			/* Background */
+			if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end())
+				oss << "," << it->second;
+
+			/* Attributes */
+			for (std::size_t i = 2; i < list.size(); ++i) {
+				auto attribute = attributesTable.find(list[i]);
+
+				if (attribute != attributesTable.end())
+					oss << attribute->second;
+			}
+		}
+	}
+
+	return oss.str();
+}
+
+std::string substitute(std::string::const_iterator &it, std::string::const_iterator &end, char token, const Substitution &params)
+{
+	std::string content, value;
+
+	if (it == end)
+		return "";
+
+	while (it != end && *it != '}')
+		content += *it++;
+
+	if (*it != '}' || it == end)
+		throw std::invalid_argument("unclosed "s + token + " construct"s);
+
+	it++;
+
+	switch (token) {
+	case '#':
+		value = substituteKeywords(content, params);
+		break;
+	case '$':
+		value = substituteEnv(content);
+		break;
+	case '@':
+		value = substituteAttributes(content);
+		break;
+	default:
+		throw std::invalid_argument("unknown "s + token + " construct");
+	}
+
+	return value;
+}
+
+} // !namespace
+
+std::string format(std::string text, const Substitution &params)
+{
+	std::ostringstream oss;
+
+	/*
+	 * Change the date format before anything else to avoid interpolation with keywords and
+	 * user input.
+	 */
+	text = substituteDate(text, params);
+
+	std::string::const_iterator it = text.begin();
+	std::string::const_iterator end = text.end();
+
+	while (it != end) {
+		auto token = *it;
+
+		if (token == '#' || token == '@' || token == '$') {
+			++ it;
+
+			if (it == end) {
+				oss << token;
+				continue;
+			}
+
+			if (*it == '{') {
+				/* Do we have a variable? */
+				oss << substitute(++it, end, token, params);
+			} else if (*it == token) {
+				/* Need one for sure */
+				oss << token;
+
+				/* Do we have a double token followed by a { for escaping? */
+				if (++it == end)
+					continue;
+
+				if (*it != '{')
+					oss << token;
+			} else {
+				oss << *it++;
+			}
+		} else {
+			oss << *it++;
+		}
+	}
+
+	return oss.str();
+}
+
+std::string strip(std::string str)
+{
+	auto test = [] (char c) { return !std::isspace(c); };
+
+	str.erase(str.begin(), std::find_if(str.begin(), str.end(), test));
+	str.erase(std::find_if(str.rbegin(), str.rend(), test).base(), str.end());
+
+	return str;
+}
+
+std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max)
+{
+	std::vector<std::string> result;
+	size_t next = -1, current;
+	int count = 1;
+	bool finished = false;
+
+	if (list.empty())
+		return result;
+
+	do {
+		std::string val;
+
+		current = next + 1;
+		next = list.find_first_of(delimiters, current);
+
+		// split max, get until the end
+		if (max >= 0 && count++ >= max) {
+			val = list.substr(current, std::string::npos);
+			finished = true;
+		} else {
+			val = list.substr(current, next - current);
+			finished = next == std::string::npos;
+		}
+
+		result.push_back(val);
+	} while (!finished);
+
+	return result;
+}
+
+MessagePair parseMessage(std::string message, const std::string &cc, const std::string &name)
+{
+	std::string result = message;
+	bool iscommand = false;
+
+	// handle special commands "!<plugin> command"
+	if (cc.length() > 0) {
+		auto pos = result.find_first_of(" \t");
+		auto fullcommand = cc + name;
+
+		/*
+		 * If the message that comes is "!foo" without spaces we
+		 * compare the command char + the plugin name. If there
+		 * is a space, we check until we find a space, if not
+		 * typing "!foo123123" will trigger foo plugin.
+		 */
+		if (pos == std::string::npos)
+			iscommand = result == fullcommand;
+		else
+			iscommand = result.length() >= fullcommand.length() && result.compare(0, pos, fullcommand) == 0;
+
+		if (iscommand) {
+			/*
+			 * If no space is found we just set the message to "" otherwise
+			 * the plugin name will be passed through onCommand
+			 */
+			if (pos == std::string::npos)
+				result = "";
+			else
+				result = message.substr(pos + 1);
+		}
+	}
+
+	return MessagePair(result, ((iscommand) ? MessageType::Command : MessageType::Message));
+}
+
+bool isBoolean(std::string value) noexcept
+{
+	return (value = unicode::toupper(value)) == "1" || value == "YES" || value == "TRUE" || value == "ON";
+}
+
+bool isInt(const std::string &str, int base) noexcept
+{
+	if (str.empty())
+		return false;
+	
+	char *ptr;
+	
+	std::strtol(str.c_str(), &ptr, base);
+	
+	return *ptr == 0;
+}
+
+bool isReal(const std::string &str) noexcept
+{
+	if (str.empty())
+		return false;
+	
+	char *ptr;
+	
+	std::strtod(str.c_str(), &ptr);
+	
+	return *ptr == 0;
+}
+
+std::string nextNetwork(std::string &input)
+{
+	std::string result;
+	std::string::size_type pos = input.find("\r\n\r\n");
+
+	if ((pos = input.find("\r\n\r\n")) != std::string::npos) {
+		result = input.substr(0, pos);
+		input.erase(input.begin(), input.begin() + pos + 4);
+	}
+
+	return result;
+}
+
+} // util
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/util.h	Thu Mar 24 14:07:30 2016 +0100
@@ -0,0 +1,246 @@
+/*
+ * util.h -- some utilities
+ *
+ * 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_UTIL_H_
+#define _IRCCD_UTIL_H_
+
+#include <ctime>
+#include <initializer_list>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace irccd {
+
+namespace util {
+
+/**
+ * @enum MessageType
+ * @brief Describe which type of message has been received
+ *
+ * On channels and queries, you may have a special command or a standard message depending on the
+ * beginning of the message.
+ *
+ * Example: `!reminder help' may invoke the command event if a plugin reminder exists.
+ */
+enum class MessageType {
+	Command,		//!< special command
+	Message			//!< standard message
+};
+
+/**
+ * @brief Combine the type of message and its content.
+ */
+using MessagePair = std::pair<std::string, MessageType>;
+
+/**
+ * @class Substitution
+ * @brief Used for format() function.
+ */
+class Substitution {
+public:
+	/**
+	 * Fill that field if you want a date.
+	 */
+	std::time_t time{std::time(nullptr)};
+
+	/**
+	 * Fill that map if you want to replace keywords.
+	 */
+	std::unordered_map<std::string, std::string> keywords;
+};
+
+/**
+ * Format a string and update all templates.
+ *
+ * Syntax
+ * ======
+ *
+ * The syntax is ?{} where ? is replaced by one of the token defined below. Braces are mandatory and cannot be ommited.
+ * To write a literal template construct, prepend the token twice.
+ *
+ * Availables templates
+ * ====================
+ *
+ * The following templates are available:
+ *
+ * - #{name}, name will be substituted from the keywords in params
+ * - ${name}, name will be substituted from the environment variable
+ * - @{attributes}, the attributes will be substituted to IRC colors (see below)
+ * - %, any format accepted by strftime(3)
+ *
+ * Attributes
+ * ==========
+ *
+ * The attribute format is composed of three parts, foreground, background and modifiers, each separated by a comma.
+ *
+ * **Note:** you cannot omit parameters, to specify the background, you must specify the foreground.
+ *
+ * Examples
+ * ========
+ *
+ * Valid constructs
+ * ----------------
+ *
+ * - `#{target}, welcome`: if target is set to "irccd", becomes "irccd, welcome",
+ * - `@{red}#{target}`: if target is specified, it is written in red,
+ *
+ * Invalid or literals constructs
+ * ------------------------------
+ *
+ * - `##{target}`: will output "#{target}",
+ * - `##`: will output "##",
+ * - `#target`: will output "#target",
+ * - `#{target`: will throw std::invalid_argument.
+ *
+ * Colors & attributes
+ * -------------------
+ *
+ * - `@{red,blue}`: will write text red on blue background,
+ * - `@{default,yellow}`: will write default color text on yellow background
+ * - `@{white,black,bold,underline}`: will write white text on black in both bold and underline
+ */
+std::string format(std::string text, const Substitution &params = {});
+
+/**
+ * Remove leading and trailing spaces.
+ *
+ * @param str the string
+ * @return the removed white spaces
+ */
+std::string strip(std::string str);
+
+/**
+ * Split a string by delimiters.
+ *
+ * @param list the string to split
+ * @param delimiter a list of delimiters
+ * @param max max number of split
+ * @return a list of string splitted
+ */
+std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max = -1);
+
+/**
+ * Join values by a separator and return a string.
+ *
+ * @param first the first iterator
+ * @param last the last iterator
+ * @param delim the optional delimiter
+ */
+template <typename InputIt, typename DelimType = char>
+std::string join(InputIt first, InputIt last, DelimType delim = ':')
+{
+	std::ostringstream oss;
+
+	if (first != last) {
+		oss << *first;
+
+		while (++first != last)
+			oss << delim << *first;
+	}
+
+	return oss.str();
+}
+
+/**
+ * Convenient overload.
+ *
+ * @param list the initializer list
+ * @param delim the delimiter
+ * @return the string
+ */
+template <typename T, typename DelimType = char>
+inline std::string join(std::initializer_list<T> list, DelimType delim = ':')
+{
+	return join(list.begin(), list.end(), delim);
+}
+
+/**
+ * Parse IRC message and determine if it's a command or a simple message.
+ *
+ * @param message the message line
+ * @param commandChar the command char (e.g '!')
+ * @param plugin the plugin name
+ * @return the pair
+ */
+MessagePair parseMessage(std::string message, const std::string &commandChar, const std::string &plugin);
+
+/**
+ * Server and identities must have strict names. This function can
+ * be used to ensure that they are valid.
+ *
+ * @param name the identifier name
+ * @return true if is valid
+ */
+inline bool isIdentifierValid(const std::string &name)
+{
+	return std::regex_match(name, std::regex("[A-Za-z0-9-_]+"));
+}
+
+/**
+ * Check if the value is a boolean, 1, yes and true are accepted.
+ *
+ * @param value the value
+ * @return true if is boolean
+ * @note this function is case-insensitive
+ */
+bool isBoolean(std::string value) noexcept;
+
+/**
+ * Check if the string is an integer.
+ *
+ * @param value the input
+ * @param base the optional base
+ * @return true if integer
+ */
+bool isInt(const std::string &value, int base = 10) noexcept;
+
+/**
+ * Check if the string is real.
+ *
+ * @param value the value
+ * @return true if real
+ */
+bool isReal(const std::string &value) noexcept;
+
+/**
+ * Check if the string is a number.
+ *
+ * @param value the value
+ * @return true if it is a number
+ */
+inline bool isNumber(const std::string &value) noexcept
+{
+	return isInt(value) || isReal(value);
+}
+
+/**
+ * Parse a network message from an input buffer and remove it from it.
+ *
+ * @param input the buffer, will be updated
+ * @return the message or empty string if there is nothing
+ */
+std::string nextNetwork(std::string &input);
+
+} // !util
+
+} // !irccd
+
+#endif // !_IRCCD_UTIL_H_