changeset 294:55662f35a16b

Irccd: get rid of lib directory, 564
author David Demelier <markand@malikania.fr>
date Thu, 06 Oct 2016 12:36:41 +0200
parents 7a82aae1ec36
children 6bf457b71e0c
files lib/irccd/alias.cpp lib/irccd/alias.hpp lib/irccd/client.cpp lib/irccd/client.hpp lib/irccd/cmd-help.cpp lib/irccd/cmd-help.hpp lib/irccd/cmd-plugin-config.cpp lib/irccd/cmd-plugin-config.hpp lib/irccd/cmd-plugin-info.cpp lib/irccd/cmd-plugin-info.hpp lib/irccd/cmd-plugin-list.cpp lib/irccd/cmd-plugin-list.hpp lib/irccd/cmd-plugin-load.cpp lib/irccd/cmd-plugin-load.hpp lib/irccd/cmd-plugin-reload.cpp lib/irccd/cmd-plugin-reload.hpp lib/irccd/cmd-plugin-unload.cpp lib/irccd/cmd-plugin-unload.hpp lib/irccd/cmd-server-cmode.cpp lib/irccd/cmd-server-cmode.hpp lib/irccd/cmd-server-cnotice.cpp lib/irccd/cmd-server-cnotice.hpp lib/irccd/cmd-server-connect.cpp lib/irccd/cmd-server-connect.hpp lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-disconnect.hpp lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-info.hpp lib/irccd/cmd-server-invite.cpp lib/irccd/cmd-server-invite.hpp lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-join.hpp lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-kick.hpp lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-list.hpp lib/irccd/cmd-server-me.cpp lib/irccd/cmd-server-me.hpp lib/irccd/cmd-server-message.cpp lib/irccd/cmd-server-message.hpp lib/irccd/cmd-server-mode.cpp lib/irccd/cmd-server-mode.hpp lib/irccd/cmd-server-nick.cpp lib/irccd/cmd-server-nick.hpp lib/irccd/cmd-server-notice.cpp lib/irccd/cmd-server-notice.hpp lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-part.hpp lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-reconnect.hpp lib/irccd/cmd-server-topic.cpp lib/irccd/cmd-server-topic.hpp lib/irccd/cmd-watch.cpp lib/irccd/cmd-watch.hpp lib/irccd/command.cpp lib/irccd/command.hpp lib/irccd/config.cpp lib/irccd/config.hpp lib/irccd/duktape.hpp lib/irccd/dynlib.hpp lib/irccd/elapsed-timer.cpp lib/irccd/elapsed-timer.hpp lib/irccd/fs.cpp lib/irccd/fs.hpp lib/irccd/ini.cpp lib/irccd/ini.hpp lib/irccd/irccd.cpp lib/irccd/irccd.hpp lib/irccd/irccdctl.cpp lib/irccd/irccdctl.hpp lib/irccd/logger.cpp lib/irccd/logger.hpp lib/irccd/mod-directory.cpp lib/irccd/mod-directory.hpp lib/irccd/mod-elapsed-timer.cpp lib/irccd/mod-elapsed-timer.hpp lib/irccd/mod-file.cpp lib/irccd/mod-file.hpp lib/irccd/mod-irccd.cpp lib/irccd/mod-irccd.hpp lib/irccd/mod-logger.cpp lib/irccd/mod-logger.hpp lib/irccd/mod-plugin.cpp lib/irccd/mod-plugin.hpp lib/irccd/mod-server.cpp lib/irccd/mod-server.hpp lib/irccd/mod-system.cpp lib/irccd/mod-system.hpp lib/irccd/mod-timer.cpp lib/irccd/mod-timer.hpp lib/irccd/mod-unicode.cpp lib/irccd/mod-unicode.hpp lib/irccd/mod-util.cpp lib/irccd/mod-util.hpp lib/irccd/module.hpp lib/irccd/net.hpp lib/irccd/options.cpp lib/irccd/options.hpp lib/irccd/path.cpp lib/irccd/path.hpp lib/irccd/plugin-dynlib.cpp lib/irccd/plugin-dynlib.hpp lib/irccd/plugin-js.cpp lib/irccd/plugin-js.hpp lib/irccd/plugin.hpp lib/irccd/rule.cpp lib/irccd/rule.hpp lib/irccd/server.cpp lib/irccd/server.hpp lib/irccd/service-command.cpp lib/irccd/service-command.hpp lib/irccd/service-interrupt.cpp lib/irccd/service-interrupt.hpp lib/irccd/service-module.cpp lib/irccd/service-module.hpp lib/irccd/service-plugin.cpp lib/irccd/service-plugin.hpp lib/irccd/service-rule.cpp lib/irccd/service-rule.hpp lib/irccd/service-server.cpp lib/irccd/service-server.hpp lib/irccd/service-transport.cpp lib/irccd/service-transport.hpp lib/irccd/signals.hpp lib/irccd/system.cpp lib/irccd/system.hpp lib/irccd/timer.cpp lib/irccd/timer.hpp lib/irccd/transport.cpp lib/irccd/transport.hpp lib/irccd/unicode.cpp lib/irccd/unicode.hpp lib/irccd/util.cpp lib/irccd/util.hpp lib/irccd/xdg.hpp
diffstat 135 files changed, 0 insertions(+), 33737 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/alias.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /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.hpp"
-
-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/lib/irccd/alias.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-/*
- * alias.hpp -- 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 IRCCD_ALIAS_HPP
-#define IRCCD_ALIAS_HPP
-
-/**
- * \file alias.hpp
- * \brief Create irccdctl aliases.
- */
-
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-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
-     */
-    IRCCD_EXPORT 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
-     */
-    IRCCD_EXPORT unsigned index() const noexcept;
-
-    /**
-     * Get the real value.
-     *
-     * \pre isPlaceholder() must return false
-     * \return the value
-     */
-    IRCCD_EXPORT const std::string &value() const noexcept;
-
-    /**
-     * Output the alias to the stream.
-     *
-     * \param out the output stream
-     * \return out
-     */
-    IRCCD_EXPORT 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 // !IRCCD_ALIAS_HPP
--- a/lib/irccd/client.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,550 +0,0 @@
-/*
- * client.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 Client WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdexcept>
-
-#include <format.h>
-
-#include "client.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-/*
- * Client::State.
- * ------------------------------------------------------------------
- */
-
-class Client::State {
-public:
-    State() = default;
-    virtual ~State() = default;
-    virtual Status status() const noexcept = 0;
-    virtual void prepare(Client &cnt, fd_set &in, fd_set &out) = 0;
-    virtual void sync(Client &cnt, fd_set &in, fd_set &out) = 0;
-};
-
-/*
- * Client::DisconnectedState.
- * ------------------------------------------------------------------
- */
-
-class Client::DisconnectedState : public Client::State {
-public:
-    Client::Status status() const noexcept override
-    {
-        return Disconnected;
-    }
-
-    void prepare(Client &, fd_set &, fd_set &) override {}
-    void sync(Client &, fd_set &, fd_set &) override {}
-};
-
-/*
- * Client::DisconnectedState.
- * ------------------------------------------------------------------
- */
-
-class Client::ReadyState : public Client::State {
-private:
-    void parse(Client &client, const std::string &message)
-    {
-        try {
-            auto json = nlohmann::json::parse(message);
-
-            if (!json.is_object())
-                return;
-
-            if (json.count("event") > 0)
-                client.onEvent(json);
-            else
-                client.onMessage(json);
-        } catch (const std::exception &) {
-        }
-    }
-public:
-    Client::Status status() const noexcept override
-    {
-        return Ready;
-    }
-
-    void prepare(Client &cnx, fd_set &in, fd_set &out) override
-    {
-        FD_SET(cnx.m_socket.handle(), &in);
-
-        if (!cnx.m_output.empty())
-            FD_SET(cnx.m_socket.handle(), &out);
-    }
-
-    void sync(Client &cnx, fd_set &in, fd_set &out) override
-    {
-        if (FD_ISSET(cnx.m_socket.handle(), &out))
-            cnx.send();
-
-        if (FD_ISSET(cnx.m_socket.handle(), &in))
-            cnx.recv();
-
-        std::string msg;
-
-        do {
-            msg = util::nextNetwork(cnx.m_input);
-
-            if (!msg.empty())
-                parse(cnx, msg);
-        } while (!msg.empty());
-    }
-};
-
-/*
- * Client::AuthState.
- * ------------------------------------------------------------------
- */
-
-class Client::AuthState : public Client::State {
-private:
-    enum {
-        Created,
-        Sending,
-        Checking
-    } m_auth{Created};
-
-    std::string m_output;
-
-    void send(Client &cnt) noexcept
-    {
-        try {
-            auto n = cnt.send(m_output.data(), m_output.size());
-
-            if (n == 0) {
-                m_output.clear();
-                throw std::runtime_error("Client lost");
-            }
-
-            m_output.erase(0, n);
-
-            if (m_output.empty())
-                m_auth = Checking;
-        } catch (const std::exception &ex) {
-            cnt.m_state = std::make_unique<DisconnectedState>();
-            cnt.onDisconnect(ex.what());
-        }
-    }
-
-    void check(Client &cnt) noexcept
-    {
-        cnt.recv();
-
-        auto msg = util::nextNetwork(cnt.m_input);
-
-        if (msg.empty())
-            return;
-
-        try {
-            auto doc = nlohmann::json::parse(msg);
-
-            if (!doc.is_object())
-                throw std::invalid_argument("invalid argument");
-
-            auto cmd = doc.find("response");
-
-            if (cmd == doc.end() || !cmd->is_string() || *cmd != "auth")
-                throw std::invalid_argument("authentication result expected");
-
-            auto result = doc.find("result");
-
-            if (result == doc.end() || !result->is_boolean())
-                throw std::invalid_argument("bad protocol");
-
-            if (!*result)
-                throw std::runtime_error("authentication failed");
-
-            cnt.m_state = std::make_unique<ReadyState>();
-        } catch (const std::exception &ex) {
-            cnt.m_state = std::make_unique<DisconnectedState>();
-            cnt.onDisconnect(ex.what());
-        }
-    }
-
-public:
-    Client::Status status() const noexcept override
-    {
-        return Authenticating;
-    }
-
-    void prepare(Client &cnt, fd_set &in, fd_set &out) override
-    {
-        switch (m_auth) {
-        case Created:
-            m_auth = Sending;
-            m_output += nlohmann::json({
-                { "command", "auth" },
-                { "password", cnt.m_password }
-            }).dump();
-            m_output += "\r\n\r\n";
-
-            // FALLTHROUGH
-        case Sending:
-            FD_SET(cnt.m_socket.handle(), &out);
-            break;
-        case Checking:
-            FD_SET(cnt.m_socket.handle(), &in);
-            break;
-        default:
-            break;
-        }
-    }
-
-    void sync(Client &cnt, fd_set &in, fd_set &out) override
-    {
-        switch (m_auth) {
-        case Sending:
-            if (FD_ISSET(cnt.m_socket.handle(), &out))
-                send(cnt);
-            break;
-        case Checking:
-            if (FD_ISSET(cnt.m_socket.handle(), &in))
-                check(cnt);
-            break;
-        default:
-            break;
-        }
-    }
-};
-
-/*
- * Client::CheckingState.
- * ------------------------------------------------------------------
- */
-
-class Client::CheckingState : public Client::State {
-private:
-    void verifyProgram(const nlohmann::json &json) const
-    {
-        auto prog = json.find("program");
-
-        if (prog == json.end() || !prog->is_string() || prog->get<std::string>() != "irccd")
-            throw std::runtime_error("not an irccd instance");
-    }
-
-    void verifyVersion(Client &cnx, const nlohmann::json &json) const
-    {
-        auto getVersionVar = [&] (auto key) {
-            auto it = json.find(key);
-
-            if (it == json.end() || !it->is_number_unsigned())
-                throw std::runtime_error("invalid irccd instance");
-
-            return *it;
-        };
-
-        Info info{
-            getVersionVar("major"),
-            getVersionVar("minor"),
-            getVersionVar("patch")
-        };
-
-        // Ensure compatibility.
-        if (info.major != IRCCD_VERSION_MAJOR || info.minor > IRCCD_VERSION_MINOR)
-            throw std::runtime_error("server version too recent {}.{}.{} vs {}.{}.{}"_format(
-                info.major, info.minor, info.patch,
-                IRCCD_VERSION_MAJOR, IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH));
-
-        // Successfully connected.
-        if (cnx.m_password.empty())
-            cnx.m_stateNext = std::make_unique<ReadyState>();
-        else
-            cnx.m_stateNext = std::make_unique<AuthState>();
-
-        cnx.onConnect(info);
-    }
-
-    void verify(Client &cnx) const
-    {
-        auto msg = util::nextNetwork(cnx.m_input);
-
-        if (msg.empty())
-            return;
-
-        try {
-            auto json = nlohmann::json::parse(msg);
-
-            verifyProgram(json);
-            verifyVersion(cnx, json);
-        } catch (const std::exception &ex) {
-            cnx.m_stateNext = std::make_unique<DisconnectedState>();
-            cnx.onDisconnect(ex.what());
-        }
-    }
-
-public:
-    Client::Status status() const noexcept override
-    {
-        return Checking;
-    }
-
-    void prepare(Client &cnx, fd_set &in, fd_set &) override
-    {
-        FD_SET(cnx.m_socket.handle(), &in);
-    }
-
-    void sync(Client &cnx, fd_set &, fd_set &) override
-    {
-        cnx.recv();
-
-        verify(cnx);
-    }
-};
-
-/*
- * Client::ConnectingState.
- * ------------------------------------------------------------------
- */
-
-class Client::ConnectingState : public Client::State {
-public:
-    Client::Status status() const noexcept override
-    {
-        return Connecting;
-    }
-
-    void prepare(Client &cnx, fd_set &, fd_set &out) override
-    {
-        FD_SET(cnx.m_socket.handle(), &out);
-    }
-
-    void sync(Client &cnx, fd_set &, fd_set &out) override
-    {
-        if (!FD_ISSET(cnx.m_socket.handle(), &out))
-            return;
-
-        try {
-            auto errc = cnx.m_socket.get<int>(SOL_SOCKET, SO_ERROR);
-
-            if (errc != 0) {
-                cnx.m_stateNext = std::make_unique<DisconnectedState>();
-                cnx.onDisconnect(net::error(errc));
-            } else
-                cnx.m_stateNext = std::make_unique<CheckingState>();
-        } catch (const std::exception &ex) {
-            cnx.m_stateNext = std::make_unique<DisconnectedState>();
-            cnx.onDisconnect(ex.what());
-        }
-    }
-};
-
-/*
- * Client.
- * ------------------------------------------------------------------
- */
-
-unsigned Client::recv(char *buffer, unsigned length)
-{
-    return m_socket.recv(buffer, length);
-}
-
-unsigned Client::send(const char *buffer, unsigned length)
-{
-    return m_socket.send(buffer, length);
-}
-
-void Client::recv()
-{
-    try {
-        std::string buffer;
-
-        buffer.resize(512);
-        buffer.resize(recv(&buffer[0], buffer.size()));
-
-        if (buffer.empty())
-            throw std::runtime_error("Client lost");
-
-        m_input += std::move(buffer);
-    } catch (const std::exception &ex) {
-        m_stateNext = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-void Client::send()
-{
-    try {
-        auto ns = send(m_output.data(), m_output.length());
-
-        if (ns > 0)
-            m_output.erase(0, ns);
-    } catch (const std::exception &ex) {
-        m_stateNext = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-Client::Client()
-    : m_state(std::make_unique<DisconnectedState>())
-{
-}
-
-Client::~Client() = default;
-
-Client::Status Client::status() const noexcept
-{
-    return m_state->status();
-}
-
-void Client::connect(const net::Address &address)
-{
-    assert(status() == Disconnected);
-
-    try {
-        m_socket = net::TcpSocket(address.domain(), 0);
-        m_socket.set(net::option::SockBlockMode(false));
-        m_socket.connect(address);
-        m_state = std::make_unique<CheckingState>();
-    } catch (const net::WouldBlockError &) {
-        m_state = std::make_unique<ConnectingState>();
-    } catch (const std::exception &ex) {
-        m_state = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-void Client::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    try {
-        m_state->prepare(*this, in, out);
-
-        if (m_socket.handle() > max)
-            max = m_socket.handle();
-    } catch (const std::exception &ex) {
-        m_state = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-void Client::sync(fd_set &in, fd_set &out)
-{
-    try {
-        m_state->sync(*this, in, out);
-
-        if (m_stateNext) {
-            m_state = std::move(m_stateNext);
-            m_stateNext = nullptr;
-        }
-    } catch (const std::exception &ex) {
-        m_state = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-/*
- * TlsClient.
- * ------------------------------------------------------------------
- */
-
-void TlsClient::handshake()
-{
-    try {
-        m_ssl->handshake();
-        m_handshake = HandshakeReady;
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    } catch (const std::exception &ex) {
-        m_state = std::make_unique<DisconnectedState>();
-        onDisconnect(ex.what());
-    }
-}
-
-unsigned TlsClient::recv(char *buffer, unsigned length)
-{
-    unsigned nread = 0;
-
-    try {
-        nread = m_ssl->recv(buffer, length);
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    }
-
-    return nread;
-}
-
-unsigned TlsClient::send(const char *buffer, unsigned length)
-{
-    unsigned nsent = 0;
-
-    try {
-        nsent = m_ssl->send(buffer, length);
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    }
-
-    return nsent;
-}
-
-void TlsClient::connect(const net::Address &address)
-{
-    Client::connect(address);
-
-    m_ssl = std::make_unique<net::TlsSocket>(m_socket, net::TlsSocket::Client);
-}
-
-void TlsClient::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    if (m_state->status() == Connecting)
-        Client::prepare(in, out, max);
-    else {
-        if (m_socket.handle() > max)
-            max = m_socket.handle();
-
-        /*
-         * Attempt an immediate handshake immediately if Client succeeded
-         * in last iteration.
-         */
-        if (m_handshake == HandshakeUndone)
-            handshake();
-
-        switch (m_handshake) {
-        case HandshakeRead:
-            FD_SET(m_socket.handle(), &in);
-            break;
-        case HandshakeWrite:
-            FD_SET(m_socket.handle(), &out);
-            break;
-        default:
-            Client::prepare(in, out, max);
-        }
-    }
-}
-
-void TlsClient::sync(fd_set &in, fd_set &out)
-{
-    if (m_state->status() == Connecting)
-        Client::sync(in, out);
-    else if (m_handshake != HandshakeReady)
-        handshake();
-    else
-        Client::sync(in, out);
-}
-
-} // !irccd
--- a/lib/irccd/client.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,320 +0,0 @@
-/*
- * client.hpp -- 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_CLIENT_HPP
-#define IRCCD_CLIENT_HPP
-
-/**
- * \file client.hpp
- * \brief Connection to irccd instance.
- */
-
-#include <cassert>
-#include <memory>
-#include <string>
-
-#include <json.hpp>
-
-#include "net.hpp"
-#include "signals.hpp"
-
-namespace irccd {
-
-/**
- * \brief Low level connection to irccd instance.
- *
- * This class is an event-based connection to an irccd instance. You can use
- * it directly if you want to issue commands to irccd in an asynchronous way.
- *
- * Being asynchronous makes mixing the event loop with this connection easier.
- *
- * It is implemented as a finite state machine as it may requires several
- * roundtrips between the controller and irccd.
- *
- * Be aware that there are no namespaces for commands, if you plan to use
- * Irccdctl class and you also connect the onMessage signal, irccdctl will also
- * use it. Do not use Irccdctl directly if this is a concern.
- *
- * The state may change and is currently implementing as following:
- *
- *   [o]
- *    |       +----------------------------+
- *    v       v                            |
- * +--------------+   +----------+     +----------------+
- * | Disconnected |-->| Checking |---->| Authenticating |
- * +--------------+   +----------+     +----------------+
- *     ^       |            ^              |
- *     |       |            |              v
- *     |       |      +------------+   +-------+
- *     |       +----->| Connecting |<--| Ready |
- *     |              +------------+   +-------+
- *     |                                   |
- *     ------------------------------------+
- */
-class Client {
-public:
-    /**
-     * \brief The current connection state.
-     */
-    enum Status {
-        Disconnected,           //!< Socket is closed
-        Connecting,             //!< Connection is in progress
-        Checking,               //!< Connection is checking irccd daemon
-        Authenticating,         //!< Connection is authenticating
-        Ready                   //!< Socket is ready for I/O
-    };
-
-    /**
-     * \brief Irccd information.
-     */
-    class Info {
-    public:
-        unsigned short major;   //!< Major version number
-        unsigned short minor;   //!< Minor version number
-        unsigned short patch;   //!< Patch version
-    };
-
-    /**
-     * onConnect
-     * --------------------------------------------------------------
-     *
-     * Connection was successful.
-     */
-    Signal<const Info &> onConnect;
-
-    /**
-     * onEvent
-     * --------------------------------------------------------------
-     *
-     * An event has been received.
-     */
-    Signal<const nlohmann::json &> onEvent;
-
-    /**
-     * onMessage
-     * ---------------------------------------------------------------
-     *
-     * A message from irccd was received.
-     */
-    Signal<const nlohmann::json &> onMessage;
-
-    /**
-     * onDisconnect
-     * --------------------------------------------------------------
-     *
-     * A fatal error occured resulting in disconnection.
-     */
-    Signal<const std::string &> onDisconnect;
-
-private:
-    std::string m_input;
-    std::string m_output;
-    std::string m_password;
-
-public:
-    class State;
-    class AuthState;
-    class DisconnectedState;
-    class ConnectingState;
-    class CheckingState;
-    class ReadyState;
-
-protected:
-    std::unique_ptr<State> m_state;
-    std::unique_ptr<State> m_stateNext;
-    net::TcpSocket m_socket{net::Invalid};
-
-    /**
-     * Try to receive some data into the given buffer.
-     *
-     * \param buffer the destination buffer
-     * \param length the buffer length
-     * \return the number of bytes received
-     */
-    virtual unsigned recv(char *buffer, unsigned length);
-
-    /**
-     * Try to send some data into the given buffer.
-     *
-     * \param buffer the source buffer
-     * \param length the buffer length
-     * \return the number of bytes sent
-     */
-    virtual unsigned send(const char *buffer, unsigned length);
-
-    /**
-     * Convenient wrapper around recv().
-     *
-     * Must be used in sync() function.
-     */
-    void recv();
-
-    /**
-     * Convenient wrapper around send().
-     *
-     * Must be used in sync() function.
-     */
-    void send();
-
-public:
-    /**
-     * Default constructor.
-     */
-    Client();
-
-    /**
-     * Default destructor.
-     */
-    virtual ~Client();
-
-    /**
-     * Get the optional password.
-     *
-     * \return the password
-     */
-    inline const std::string &password() const noexcept
-    {
-        return m_password;
-    }
-
-    /**
-     * Set the optional password
-     *
-     * \param password the password
-     */
-    inline void setPassword(std::string password) noexcept
-    {
-        m_password = std::move(password);
-    }
-
-    /**
-     * Send an asynchronous request to irccd.
-     *
-     * \pre json.is_object
-     * \param json the JSON object
-     */
-    inline void request(const nlohmann::json &json)
-    {
-        assert(json.is_object());
-
-        m_output += json.dump();
-        m_output += "\r\n\r\n";
-    }
-
-    /**
-     * Get the underlying socket handle.
-     *
-     * \return the handle
-     */
-    inline net::Handle handle() const noexcept
-    {
-        return m_socket.handle();
-    }
-
-    /**
-     * Shorthand for state() != Disconnected.
-     *
-     * \return true if state() != Disconnected
-     */
-    inline bool isConnected() const noexcept
-    {
-        return status() != Disconnected;
-    }
-
-    /**
-     * Get the current state.
-     *
-     * \return the state
-     */
-    Status status() const noexcept;
-
-    /**
-     * Initiate connection to irccd.
-     *
-     * \pre state() == Disconnected
-     * \param address the address
-     */
-    virtual void connect(const net::Address &address);
-
-    /**
-     * Prepare the input and output set according to the current connection
-     * state.
-     *
-     * \param in the input set
-     * \param out the output set
-     * \param max the maximum file descriptor
-     */
-    virtual void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * Do some I/O using the protected recv and send functions.
-     *
-     * \param in the input set
-     * \param out the output set
-     */
-    virtual void sync(fd_set &in, fd_set &out);
-};
-
-/**
- * \brief TLS over IP connection.
- */
-class TlsClient : public Client {
-private:
-    enum {
-        HandshakeUndone,
-        HandshakeRead,
-        HandshakeWrite,
-        HandshakeReady
-    } m_handshake{HandshakeUndone};
-
-private:
-    std::unique_ptr<net::TlsSocket> m_ssl;
-
-    void handshake();
-
-protected:
-    /**
-     * \copydoc Client::recv
-     */
-    virtual unsigned recv(char *buffer, unsigned length);
-
-    /**
-     * \copydoc Client::send
-     */
-    virtual unsigned send(const char *buffer, unsigned length);
-
-public:
-    /**
-     * \copydoc Client::connect
-     */
-    void connect(const net::Address &address) override;
-
-    /**
-     * \copydoc Service::prepare
-     */
-    void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
-
-    /**
-     * \copydoc Service::sync
-     */
-    void sync(fd_set &in, fd_set &out) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_CLIENT_HPP
--- a/lib/irccd/cmd-help.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * cmd-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 "cmd-help.hpp"
-#include "irccdctl.hpp"
-#include "logger.hpp"
-
-namespace irccd {
-
-namespace command {
-
-HelpCommand::HelpCommand()
-    : Command("help", "General", "Get help about a command")
-{
-}
-
-std::vector<Command::Arg> HelpCommand::args() const
-{
-    return {{ "command", true }};
-}
-
-nlohmann::json HelpCommand::request(Irccdctl &irccdctl, const CommandRequest &args) const
-{
-    auto it = irccdctl.commandService().find(args.arg(0U));
-
-    if (!it)
-        log::warning() << "there is no command named: " << args.arg(0U) << std::endl;
-    else
-        log::warning() << it->help() << std::flush;
-
-    return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-help.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * cmd-help.hpp -- 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 IRCCD_CMD_HPPELP_HPP
-#define IRCCD_CMD_HPPELP_HPP
-
-/**
- * \file cmd-help.hpp
- * \brief Implementation of irccdctl help.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \class Help
- * \brief Implementation of irccdctl help.
- */
-class HelpCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT HelpCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_HPPELP_HPP
--- a/lib/irccd/cmd-plugin-config.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * cmd-plugin-config.cpp -- implementation of plugin-config 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 <iomanip>
-#include <iostream>
-
-#include "irccd.hpp"
-#include "cmd-plugin-config.hpp"
-#include "service-plugin.hpp"
-
-namespace irccd {
-
-namespace command {
-
-namespace {
-
-nlohmann::json execSet(Irccd &irccd, const nlohmann::json &request, const std::string &var, const std::string &value)
-{
-    auto plugin = irccd.plugins().require(request["plugin"].get<std::string>());
-    auto config = plugin->config();
-
-    config[var] = value;
-    plugin->setConfig(config);
-
-    return nullptr;
-}
-
-nlohmann::json execGet(Irccd &irccd, const nlohmann::json &request, const nlohmann::json::const_iterator &var)
-{
-    auto config = irccd.plugins().require(request["plugin"].get<std::string>())->config();
-
-    // 'vars' property.
-    std::map<std::string, nlohmann::json> vars;
-
-    if (var != request.end())
-        vars.emplace(var->get<std::string>(), config[var->get<std::string>()]);
-    else
-        for (const auto &pair : config)
-            vars.emplace(pair.first, pair.second);
-
-    return nlohmann::json::object({{ "variables", nlohmann::json(vars) }});
-}
-
-} // !namespace
-
-PluginConfigCommand::PluginConfigCommand()
-    : Command("plugin-config", "Plugins", "Get or set a plugin config variable")
-{
-}
-
-std::vector<Command::Arg> PluginConfigCommand::args() const
-{
-    return {
-        { "plugin",     true    },
-        { "variable",   false   },
-        { "value",      false   }
-    };
-}
-
-std::vector<Command::Property> PluginConfigCommand::properties() const
-{
-    return {{ "plugin", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json PluginConfigCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    auto object = nlohmann::json::object({
-        { "plugin", args.arg(0) }
-    });
-
-    if (args.length() >= 2U) {
-        object.push_back({"variable", args.arg(1)});
-
-        if (args.length() == 3U)
-            object.push_back({"value", args.arg(2)});
-    }
-
-    return object;
-}
-
-nlohmann::json PluginConfigCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    auto var = request.find("variable");
-
-    if (var != request.end() && var->is_string())
-        throw InvalidPropertyError("variable", nlohmann::json::value_t::string, var->type());
-
-    auto value = request.find("value");
-
-    if (value != request.end())
-        return execSet(irccd, request, var->dump(), value->dump());
-
-    return execGet(irccd, request, var);
-}
-
-void PluginConfigCommand::result(Irccdctl &irccdctl, const nlohmann::json &response) const
-{
-    Command::result(irccdctl, response);
-
-    auto it = response.find("variables");
-
-    if (it == response.end() || !it->is_object())
-        return;
-
-    if (it->size() > 1U)
-        for (auto v = it->begin(); v != it->end(); ++v)
-            std::cout << std::setw(16) << std::left << v.key() << " : " << v->dump() << std::endl;
-    else
-        std::cout << it->begin()->dump() << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-plugin-config.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * cmd-plugin-config.hpp -- implementation of plugin-config 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_CMD_PLUGIN_CONFIG_HPP
-#define IRCCD_CMD_PLUGIN_CONFIG_HPP
-
-/**
- * \file cmd-plugin-config.hpp
- * \brief Implementation of plugin-config transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-config transport command.
- */
-class PluginConfigCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginConfigCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-
-    /**
-     * \copydoc Command::result
-     */
-    IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_CONFIG_HPP
--- a/lib/irccd/cmd-plugin-info.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * cmd-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 "cmd-plugin-info.hpp"
-#include "irccd.hpp"
-#include "plugin.hpp"
-#include "service-plugin.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace command {
-
-PluginInfoCommand::PluginInfoCommand()
-    : Command("plugin-info", "Plugins", "Get plugin information")
-{
-}
-
-std::vector<Command::Arg> PluginInfoCommand::args() const
-{
-    return {{ "plugin", true }};
-}
-
-std::vector<Command::Property> PluginInfoCommand::properties() const
-{
-    return {{ "plugin", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json PluginInfoCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({{ "plugin", args.arg(0) }});
-}
-
-nlohmann::json PluginInfoCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    auto plugin = irccd.plugins().require(request.at("plugin").get<std::string>());
-
-    return nlohmann::json::object({
-        { "author",     plugin->author()    },
-        { "license",    plugin->license()   },
-        { "summary",    plugin->summary()   },
-        { "version",    plugin->version()   }
-    });
-}
-
-void PluginInfoCommand::result(Irccdctl &irccdctl, const nlohmann::json &result) const
-{
-    Command::result(irccdctl, result);
-
-    auto it = result.find("status");
-
-    if (!it->is_boolean() || !*it)
-        return;
-
-    auto get = [&] (auto key) -> std::string {
-        auto v = result.find(key);
-
-        if (v == result.end() || !v->is_primitive())
-            return "";
-
-        return v->dump();
-    };
-
-    std::cout << std::boolalpha;
-    std::cout << "Author         : " << get("author") << std::endl;
-    std::cout << "License        : " << get("license") << std::endl;
-    std::cout << "Summary        : " << get("summary") << std::endl;
-    std::cout << "Version        : " << get("version") << std::endl;
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/cmd-plugin-info.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * cmd-plugin-info.hpp -- 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_CMD_PLUGIN_INFO_HPP
-#define IRCCD_CMD_PLUGIN_INFO_HPP
-
-/**
- * \file cmd-plugin-info.hpp
- * \brief Implementation of plugin-info transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-info transport command.
- */
-class PluginInfoCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginInfoCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-
-    /**
-     * \copydoc Command::result
-     */
-    IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_INFO_HPP
--- a/lib/irccd/cmd-plugin-list.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * cmd-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 "cmd-plugin-list.hpp"
-#include "irccd.hpp"
-#include "plugin.hpp"
-#include "service-plugin.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace command {
-
-PluginListCommand::PluginListCommand()
-    : Command("plugin-list", "Plugins", "Get the list of loaded plugins")
-{
-}
-
-nlohmann::json PluginListCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    auto response = Command::exec(irccd, request);
-    auto list = nlohmann::json::array();
-
-    for (const auto &plugin : irccd.plugins().list())
-        list += plugin->name();
-
-    response.push_back({"list", std::move(list)});
-
-    return response;
-}
-
-void PluginListCommand::result(Irccdctl &irccdctl, const nlohmann::json &object) const
-{
-    Command::result(irccdctl, object);
-
-    auto it = object.find("list");
-
-    if (it != object.end() && it->is_array())
-        for (const auto &n : *it)
-            std::cout << n.dump() << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-plugin-list.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * cmd-plugin-list.hpp -- 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_CMD_PLUGIN_LIST_HPP
-#define IRCCD_CMD_PLUGIN_LIST_HPP
-
-/**
- * \file cmd-plugin-list.hpp
- * \brief Implementation of plugin-list transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-list transport command.
- */
-class PluginListCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginListCommand();
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-
-    /**
-     * \copydoc Command::result
-     */
-    IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_LIST_HPP
--- a/lib/irccd/cmd-plugin-load.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * cmd-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 "cmd-plugin-load.hpp"
-#include "irccd.hpp"
-#include "service-plugin.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace command {
-
-PluginLoadCommand::PluginLoadCommand()
-    : Command("plugin-load", "Plugins", "Load a plugin")
-{
-}
-
-std::vector<Command::Arg> PluginLoadCommand::args() const
-{
-    return {{ "plugin", true }};
-}
-
-std::vector<Command::Property> PluginLoadCommand::properties() const
-{
-    return {{ "plugin", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json PluginLoadCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({{ "plugin", args.arg(0) }});
-}
-
-nlohmann::json PluginLoadCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.plugins().load(request["plugin"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-plugin-load.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-plugin-load.hpp -- 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_CMD_PLUGIN_LOAD_HPP
-#define IRCCD_CMD_PLUGIN_LOAD_HPP
-
-/**
- * \file cmd-plugin-load.hpp
- * \brief Implementation of plugin-load transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-load transport command.
- */
-class PluginLoadCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginLoadCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_LOAD_HPP
--- a/lib/irccd/cmd-plugin-reload.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * cmd-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 "cmd-plugin-reload.hpp"
-#include "irccd.hpp"
-#include "plugin.hpp"
-#include "service-plugin.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace command {
-
-PluginReloadCommand::PluginReloadCommand()
-    : Command("plugin-reload", "Plugins", "Reload a plugin")
-{
-}
-
-std::vector<Command::Arg> PluginReloadCommand::args() const
-{
-    return {{ "plugin", true }};
-}
-
-std::vector<Command::Property> PluginReloadCommand::properties() const
-{
-    return {{ "plugin", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json PluginReloadCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({{ "plugin", args.arg(0) }});
-}
-
-nlohmann::json PluginReloadCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.plugins().require(request["plugin"])->onReload(irccd);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-plugin-reload.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-plugin-reload.hpp -- 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_CMD_PLUGIN_RELOAD_HPP
-#define IRCCD_CMD_PLUGIN_RELOAD_HPP
-
-/**
- * \file cmd-plugin-reload.hpp
- * \brief Implementation of plugin-reload transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-reload transport command.
- */
-class PluginReloadCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginReloadCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_RELOAD_HPP
--- a/lib/irccd/cmd-plugin-unload.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * cmd-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 "cmd-plugin-unload.hpp"
-#include "irccd.hpp"
-#include "service-plugin.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace command {
-
-PluginUnloadCommand::PluginUnloadCommand()
-    : Command("plugin-unload", "Plugins", "Unload a plugin")
-{
-}
-
-std::vector<Command::Arg> PluginUnloadCommand::args() const
-{
-    return {{ "plugin", true }};
-}
-
-std::vector<Command::Property> PluginUnloadCommand::properties() const
-{
-    return {{ "plugin", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json PluginUnloadCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({{ "plugin", args.arg(0) }});
-}
-
-nlohmann::json PluginUnloadCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.plugins().unload(request["plugin"].get<std::string>());
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-plugin-unload.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-plugin-unload.hpp -- 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_CMD_PLUGIN_UNLOAD_HPP
-#define IRCCD_CMD_PLUGIN_UNLOAD_HPP
-
-/**
- * \file cmd-plugin-unload.hpp
- * \brief Implementation of plugin-unload transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of plugin-unload transport command.
- */
-class PluginUnloadCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT PluginUnloadCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_PLUGIN_UNLOAD_HPP
--- a/lib/irccd/cmd-server-cmode.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * cmd-server-cmode.cpp -- implementation of server-cmode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-cmode.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerChannelModeCommand::ServerChannelModeCommand()
-    : Command("server-cmode", "Server", "Change a channel mode")
-{
-}
-
-std::vector<Command::Arg> ServerChannelModeCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "channel",    true },
-        { "mode",       true }
-    };
-}
-
-std::vector<Command::Property> ServerChannelModeCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }},
-        { "mode",       { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerChannelModeCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"].get<std::string>())->cmode(
-        request["channel"].get<std::string>(),
-        request["mode"].get<std::string>()
-    );
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/cmd-server-cmode.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * cmd-server-cmode.hpp -- 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_CMD_SERVER_CMODE_HPP
-#define IRCCD_CMD_SERVER_CMODE_HPP
-
-/**
- * \file cmd-server-cmode.hpp
- * \brief Implementation of server-cmode transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-cmode transport command.
- */
-class ServerChannelModeCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerChannelModeCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_CMODE_HPP
--- a/lib/irccd/cmd-server-cnotice.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * cmd-server-cnotice.cpp -- implementation of server-cnotice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-cnotice.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerChannelNoticeCommand::ServerChannelNoticeCommand()
-    : Command("server-cnotice", "Server", "Send a channel notice")
-{
-}
-
-std::vector<Command::Arg> ServerChannelNoticeCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "channel",    true },
-        { "message",    true }
-    };
-}
-
-std::vector<Command::Property> ServerChannelNoticeCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }},
-        { "message",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerChannelNoticeCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"].get<std::string>())->cnotice(
-        request["channel"].get<std::string>(),
-        request["message"].get<std::string>()
-    );
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-cnotice.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * cmd-server-cnotice.hpp -- 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_CMD_SERVER_CNOTICE_HPP
-#define IRCCD_CMD_SERVER_CNOTICE_HPP
-
-/**
- * \file cmd-server-cnotice.hpp
- * \brief Implementation of server-cnotice transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \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 ServerChannelNoticeCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerChannelNoticeCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_CNOTICE_HPP
--- a/lib/irccd/cmd-server-connect.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * cmd-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 <format.h>
-
-#include "cmd-server-connect.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-
-using json = nlohmann::json;
-
-namespace irccd {
-
-namespace command {
-
-ServerConnectCommand::ServerConnectCommand()
-    : Command("server-connect", "Server", "Connect to a server")
-{
-}
-
-std::vector<Command::Option> ServerConnectCommand::options() const
-{
-    return {
-        { "command",    "c", "command",     "char",     "command character to use"  },
-        { "nickname",   "n", "nickname",    "nickname", "nickname to use"           },
-        { "realname",   "r", "realname",    "realname", "realname to use"           },
-        { "sslverify",  "S", "ssl-verify",  "",         "verify SSL"                },
-        { "ssl",        "s", "ssl",         "",         "connect with SSL"          },
-        { "username",   "u", "username",    "",         "username to use"           }
-    };
-}
-
-std::vector<Command::Arg> ServerConnectCommand::args() const
-{
-    return {
-        { "id",     true    },
-        { "host",   true    },
-        { "port",   false   }
-    };
-}
-
-std::vector<Command::Property> ServerConnectCommand::properties() const
-{
-    return {
-        { "name",   { json::value_t::string }},
-        { "host",   { json::value_t::string }}
-    };
-}
-
-json ServerConnectCommand::exec(Irccd &irccd, const json &request) const
-{
-    auto server = Server::fromJson(request);
-
-    if (irccd.servers().has(server->name()))
-        throw std::invalid_argument("server '{}' already exists"_format(server->name()));
-
-    irccd.servers().add(std::move(server));
-
-    return Command::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-connect.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-connect.hpp -- 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_CMD_SERVER_CONNECT_HPP
-#define IRCCD_CMD_SERVER_CONNECT_HPP
-
-/**
- * \file cmd-server-connect.hpp
- * \brief Implementation of server-connect transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-connect transport command.
- */
-class ServerConnectCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerConnectCommand();
-
-    /**
-     * \copydoc Command::options
-     */
-    IRCCD_EXPORT std::vector<Option> options() const override;
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_CONNECT_HPP
--- a/lib/irccd/cmd-server-disconnect.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * cmd-server-disconnect.cpp -- implementation of server-disconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-disconnect.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerDisconnectCommand::ServerDisconnectCommand()
-    : Command("server-disconnect", "Server", "Disconnect one or more servers")
-{
-}
-
-std::vector<Command::Arg> ServerDisconnectCommand::args() const
-{
-    return {{ "server", false }};
-}
-
-nlohmann::json ServerDisconnectCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    auto it = request.find("server");
-
-    if (it == request.end())
-        irccd.servers().clear();
-    else
-        irccd.servers().remove(*it);
-
-    return Command::exec(irccd, request);
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-disconnect.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * cmd-server-disconnect.hpp -- 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_CMD_SERVER_DISCONNECT_HPP
-#define IRCCD_CMD_SERVER_DISCONNECT_HPP
-
-/**
- * \file cmd-server-disconnect.hpp
- * \brief Implementation of server-disconnect transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-disconnect transport command.
- */
-class ServerDisconnectCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerDisconnectCommand();
-
-    /**
-     * Get list of arguments required.
-     *
-     * \return the arguments required
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_DISCONNECT_HPP
--- a/lib/irccd/cmd-server-info.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
- * cmd-server-info.cpp -- implementation of server-info transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include "cmd-server-info.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerInfoCommand::ServerInfoCommand()
-    : Command("server-info", "Server", "Get server information")
-{
-}
-
-std::vector<Command::Arg> ServerInfoCommand::args() const
-{
-    return {{ "server", true }};
-}
-
-std::vector<Command::Property> ServerInfoCommand::properties() const
-{
-    return {{ "server", { nlohmann::json::value_t::string }}};
-}
-
-nlohmann::json ServerInfoCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return {{ "server", args.args()[0] }};
-}
-
-nlohmann::json ServerInfoCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    auto response = Command::exec(irccd, request);
-    auto server = irccd.servers().require(request["server"]);
-
-    // General stuff.
-    response.push_back({"name", server->name()});
-    response.push_back({"host", server->host()});
-    response.push_back({"port", server->port()});
-    response.push_back({"nickname", server->nickname()});
-    response.push_back({"username", server->username()});
-    response.push_back({"realname", server->realname()});
-    response.push_back({"channels", server->channels()});
-
-    // Optional stuff.
-    if (server->flags() & Server::Ipv6)
-        response.push_back({"ipv6", true});
-    if (server->flags() & Server::Ssl)
-        response.push_back({"ssl", true});
-    if (server->flags() & Server::SslVerify)
-        response.push_back({"sslVerify", true});
-
-    return response;
-}
-
-void ServerInfoCommand::result(Irccdctl &irccdctl, const nlohmann::json &response) const
-{
-    Command::result(irccdctl, response);
-
-    auto get = [&] (auto key) -> std::string {
-        auto v = response.find(key);
-
-        if (v == response.end() || !v->is_primitive())
-            return "";
-
-        return v->dump();
-    };
-
-    // Server information.
-    std::cout << std::boolalpha;
-    std::cout << "Name           : " << get("name") << std::endl;
-    std::cout << "Host           : " << get("host") << std::endl;
-    std::cout << "Port           : " << get("port") << std::endl;
-    std::cout << "Ipv6           : " << get("ipv6") << std::endl;
-    std::cout << "SSL            : " << get("ssl") << std::endl;
-    std::cout << "SSL verified   : " << get("sslVerify") << std::endl;
-
-    // Channels.
-    std::cout << "Channels       : ";
-
-    if (response.count("channels") != 0)
-        for (const auto &v : response["channels"])
-            std::cout << v.dump() << " ";
-
-    std::cout << std::endl;
-
-    // Identity.
-    std::cout << "Nickname       : " << get("nickname") << std::endl;
-    std::cout << "User name      : " << get("username") << std::endl;
-    std::cout << "Real name      : " << get("realname") << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-info.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * cmd-server-info.hpp -- 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_CMD_SERVER_INFO_HPP
-#define IRCCD_CMD_SERVER_INFO_HPP
-
-/**
- * \file cmd-server-info.hpp
- * \brief Implementation of server-info transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-info transport command.
- */
-class ServerInfoCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerInfoCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-
-    /**
-     * \copydoc Command::result
-     */
-    IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_INFO_HPP
--- a/lib/irccd/cmd-server-invite.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * cmd-server-invite.cpp -- implementation of server-invite transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-invite.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerInviteCommand::ServerInviteCommand()
-    : Command("server-invite", "Server", "Invite someone into a channel")
-{
-}
-
-std::vector<Command::Arg> ServerInviteCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "nickname",   true },
-        { "channel",    true }
-    };
-}
-
-std::vector<Command::Property> ServerInviteCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "target",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerInviteCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.args()[0] },
-        { "target",     args.args()[1] },
-        { "channel",    args.args()[2] }
-    });
-}
-
-nlohmann::json ServerInviteCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->invite(request["target"], request["channel"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
-
--- a/lib/irccd/cmd-server-invite.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-invite.hpp -- 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_CMD_SERVER_INVITE_HPP
-#define IRCCD_CMD_SERVER_INVITE_HPP
-
-/**
- * \file cmd-server-invite.hpp
- * \brief Implementation of server-invite transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-invite transport command.
- */
-class ServerInviteCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerInviteCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_INVITE_HPP
--- a/lib/irccd/cmd-server-join.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * cmd-server-join.cpp -- implementation of server-join transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-join.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerJoinCommand::ServerJoinCommand()
-    : Command("server-join", "Server", "Join a channel")
-{
-}
-
-std::vector<Command::Arg> ServerJoinCommand::args() const
-{
-    return {
-        { "server",     true    },
-        { "channel",    true    },
-        { "password",   false   }
-    };
-}
-
-std::vector<Command::Property> ServerJoinCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerJoinCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    auto req = nlohmann::json::object({
-        { "server",     args.args()[0] },
-        { "channel",    args.args()[1] }
-    });
-
-    if (args.length() == 3)
-        req.push_back({"password", args.args()[2]});
-
-    return req;
-}
-
-nlohmann::json ServerJoinCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    std::string password;
-
-    if (request.find("password") != request.end())
-        password = request["password"];
-
-    irccd.servers().require(
-        request.at("server").get<std::string>())->join(
-        request.at("channel").get<std::string>(),
-        password
-    );
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-join.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-join.hpp -- 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_CMD_SERVER_JOIN_HPP
-#define IRCCD_CMD_SERVER_JOIN_HPP
-
-/**
- * \file cmd-server-join.hpp
- * \brief Implementation of server-join transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-join transport command.
- */
-class ServerJoinCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerJoinCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_JOIN_HPP
--- a/lib/irccd/cmd-server-kick.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * cmd-server-kick.cpp -- implementation of server-kick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-kick.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerKickCommand::ServerKickCommand()
-    : Command("server-kick", "Server", "Kick someone from a channel")
-{
-}
-
-std::vector<Command::Arg> ServerKickCommand::args() const
-{
-    return {
-        { "server",     true    },
-        { "target",     true    },
-        { "channel",    true    },
-        { "reason",     false   }
-    };
-}
-
-std::vector<Command::Property> ServerKickCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "target",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerKickCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    auto req = nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "target",     args.arg(1) },
-        { "channel",    args.arg(2) }
-    });
-
-    if (args.length() == 4)
-        req.push_back({"reason", args.arg(3)});
-
-    return req;
-}
-
-nlohmann::json ServerKickCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->kick(
-        request["target"],
-        request["channel"],
-        request.count("reason") > 0 ? request["reason"] : ""
-    );
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-kick.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-kick.hpp -- 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_CMD_SERVER_KICK_HPP
-#define IRCCD_CMD_SERVER_KICK_HPP
-
-/**
- * \file cmd-server-kick.hpp
- * \brief Implementation of server-kick transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-kick transport command.
- */
-class ServerKickCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerKickCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_KICK_HPP
--- a/lib/irccd/cmd-server-list.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * cmd-server-list.cpp -- implementation of server-list transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include "cmd-server-list.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerListCommand::ServerListCommand()
-    : Command("server-list", "Server", "Get the list of servers")
-{
-}
-
-nlohmann::json ServerListCommand::exec(Irccd &irccd, const nlohmann::json &) const
-{
-    auto json = nlohmann::json::object();
-    auto list = nlohmann::json::array();
-
-    for (const auto &server : irccd.servers().servers())
-        list.push_back(server->name());
-
-    json.push_back({"list", std::move(list)});
-
-    return json;
-}
-
-void ServerListCommand::result(Irccdctl &, const nlohmann::json &response) const
-{
-    auto list = response.find("list");
-
-    if (list == response.end())
-        return;
-
-    for (auto v : *list)
-        if (v.is_string())
-            std::cout << v.get<std::string>() << std::endl;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-list.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * cmd-server-list.hpp -- 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_CMD_SERVER_LIST_HPP
-#define IRCCD_CMD_SERVER_LIST_HPP
-
-/**
- * \file cmd-server-list.hpp
- * \brief Implementation of server-list transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-list transport command.
- */
-class ServerListCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerListCommand();
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-
-    /**
-     * \copydoc Command::result
-     */
-    IRCCD_EXPORT void result(Irccdctl &irccdctl, const nlohmann::json &response) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_LIST_HPP
--- a/lib/irccd/cmd-server-me.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * cmd-server-me.cpp -- implementation of server-me transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-me.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerMeCommand::ServerMeCommand()
-    : Command("server-me", "Server", "Send an action emote")
-{
-}
-
-std::vector<Command::Arg> ServerMeCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "target",     true },
-        { "message",    true }
-    };
-}
-
-std::vector<Command::Property> ServerMeCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "target",     { nlohmann::json::value_t::string }},
-        { "message",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerMeCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "target",     args.arg(1) },
-        { "message",    args.arg(2) }
-    });
-}
-
-nlohmann::json ServerMeCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->me(request["target"], request["message"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-me.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-me.hpp -- 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_CMD_SERVER_ME_HPP
-#define IRCCD_CMD_SERVER_ME_HPP
-
-/**
- * \file cmd-server-me.hpp
- * \brief Implementation of server-me transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-me transport command.
- */
-class ServerMeCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerMeCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_ME_HPP
--- a/lib/irccd/cmd-server-message.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * cmd-server-message.cpp -- implementation of server-message transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-message.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerMessageCommand::ServerMessageCommand()
-    : Command("server-message", "Server", "Send a message")
-{
-}
-
-std::vector<Command::Arg> ServerMessageCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "target",     true },
-        { "message",    true }
-    };
-}
-
-std::vector<Command::Property> ServerMessageCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "target",     { nlohmann::json::value_t::string }},
-        { "message",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerMessageCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "target",     args.arg(1) },
-        { "message",    args.arg(2) }
-    });
-}
-
-nlohmann::json ServerMessageCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->me(request["target"], request["message"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-message.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-message.hpp -- 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_CMD_SERVER_MESSAGE_HPP
-#define IRCCD_CMD_SERVER_MESSAGE_HPP
-
-/**
- * \file cmd-server-message.hpp
- * \brief Implementation of server-message transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-message transport command.
- */
-class ServerMessageCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerMessageCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_MESSAGE_HPP
--- a/lib/irccd/cmd-server-mode.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-mode.cpp -- implementation of server-mode transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-mode.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerModeCommand::ServerModeCommand()
-    : Command("server-mode", "Server", "Change your mode")
-{
-}
-
-std::vector<Command::Arg> ServerModeCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "mode",       true }
-    };
-}
-
-std::vector<Command::Property> ServerModeCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "mode",       { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerModeCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "mode",       args.arg(1) }
-    });
-}
-
-nlohmann::json ServerModeCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->mode(request["mode"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-mode.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-mode.hpp -- 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_CMD_SERVER_MODE_HPP
-#define IRCCD_CMD_SERVER_MODE_HPP
-
-/**
- * \file cmd-server-mode.hpp
- * \brief Implementation of server-mode transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-mode transport command.
- */
-class ServerModeCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerModeCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_MODE_HPP
--- a/lib/irccd/cmd-server-nick.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-nick.cpp -- implementation of server-nick transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-nick.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerNickCommand::ServerNickCommand()
-    : Command("server-nick", "Server", "Change your nickname")
-{
-}
-
-std::vector<Command::Arg> ServerNickCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "nickname",   true }
-    };
-}
-
-std::vector<Command::Property> ServerNickCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "nickname",   { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerNickCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "nickname",   args.arg(1) }
-    });
-}
-
-nlohmann::json ServerNickCommand::exec(Irccd &irccd, const nlohmann::json &object) const
-{
-    Command::exec(irccd, object);
-
-    irccd.servers().require(object["server"])->setNickname(object["nickname"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-nick.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-nick.hpp -- 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_CMD_SERVER_NICK_HPP
-#define IRCCD_CMD_SERVER_NICK_HPP
-
-/**
- * \file cmd-server-nick.hpp
- * \brief Implementation of server-nick transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-nick transport command.
- */
-class ServerNickCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerNickCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_NICK_HPP
--- a/lib/irccd/cmd-server-notice.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * cmd-server-notice.cpp -- implementation of server-notice transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-notice.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerNoticeCommand::ServerNoticeCommand()
-    : Command("server-notice", "Server", "Send a private notice")
-{
-}
-
-std::vector<Command::Arg> ServerNoticeCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "target",     true },
-        { "message",    true }
-    };
-}
-
-std::vector<Command::Property> ServerNoticeCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "target",     { nlohmann::json::value_t::string }},
-        { "message",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerNoticeCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "target",     args.arg(1) },
-        { "message",    args.arg(2) }
-    });
-}
-
-nlohmann::json ServerNoticeCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->notice(request["target"], request["message"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-notice.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-notice.hpp -- 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_CMD_SERVER_NOTICE_HPP
-#define IRCCD_CMD_SERVER_NOTICE_HPP
-
-/**
- * \file cmd-server-notice.hpp
- * \brief Implementation of server-notice transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-notice transport command.
- */
-class ServerNoticeCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerNoticeCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_NOTICE_HPP
--- a/lib/irccd/cmd-server-part.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * cmd-server-part.cpp -- implementation of server-part transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-part.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerPartCommand::ServerPartCommand()
-    : Command("server-part", "Server", "Leave a channel")
-{
-}
-
-std::vector<Command::Arg> ServerPartCommand::args() const
-{
-    return {
-        { "server",     true    },
-        { "channel",    true    },
-        { "reason",     false    }
-    };
-}
-
-std::vector<Command::Property> ServerPartCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerPartCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    auto req = nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "channel",    args.arg(1) }
-    });
-
-    if (args.length() == 3)
-        req.push_back({"reason", args.arg(2)});
-
-    return req;
-}
-
-nlohmann::json ServerPartCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->part(
-        request["channel"],
-        request.count("reason") > 0 ? request["reason"] : ""
-    );
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-part.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * cmd-server-part.hpp -- 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_CMD_SERVER_PART_HPP
-#define IRCCD_CMD_SERVER_PART_HPP
-
-/**
- * \file cmd-server-part.hpp
- * \brief Implementation of server-part transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \class ServerPart
- * \brief Implementation of server-part transport command.
- */
-class ServerPartCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerPartCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_PART_HPP
--- a/lib/irccd/cmd-server-reconnect.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * cmd-server-reconnect.cpp -- implementation of server-reconnect transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-reconnect.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerReconnectCommand::ServerReconnectCommand()
-    : Command("server-reconnect", "Server", "Force reconnection of one or more servers")
-{
-}
-
-std::vector<Command::Arg> ServerReconnectCommand::args() const
-{
-    return {{ "server", false }};
-}
-
-nlohmann::json ServerReconnectCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return args.length() == 0 ? nlohmann::json::object() : nlohmann::json::object({ { "server", args.arg(0) } });
-}
-
-nlohmann::json ServerReconnectCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    auto server = request.find("server");
-
-    if (server != request.end() && server->is_string())
-        irccd.servers().require(*server)->reconnect();
-    else
-        for (auto &server : irccd.servers().servers())
-            server->reconnect();
-
-    return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-reconnect.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * cmd-server-reconnect.hpp -- 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_CMD_SERVER_RECONNECT_HPP
-#define IRCCD_CMD_SERVER_RECONNECT_HPP
-
-/**
- * \file cmd-server-reconnect.hpp
- * \brief Implementation of server-reconnect transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-reconnect transport command.
- */
-class ServerReconnectCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerReconnectCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_RECONNECT_HPP
--- a/lib/irccd/cmd-server-topic.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * cmd-server-topic.cpp -- implementation of server-topic transport command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cmd-server-topic.hpp"
-#include "irccd.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace command {
-
-ServerTopicCommand::ServerTopicCommand()
-    : Command("server-topic", "Server", "Change a channel topic")
-{
-}
-
-std::vector<Command::Arg> ServerTopicCommand::args() const
-{
-    return {
-        { "server",     true },
-        { "channel",    true },
-        { "topic",      true }
-    };
-}
-
-std::vector<Command::Property> ServerTopicCommand::properties() const
-{
-    return {
-        { "server",     { nlohmann::json::value_t::string }},
-        { "channel",    { nlohmann::json::value_t::string }},
-        { "topic",      { nlohmann::json::value_t::string }}
-    };
-}
-
-nlohmann::json ServerTopicCommand::request(Irccdctl &, const CommandRequest &args) const
-{
-    return nlohmann::json::object({
-        { "server",     args.arg(0) },
-        { "channel",    args.arg(1) },
-        { "topic",      args.arg(2) }
-    });
-}
-
-nlohmann::json ServerTopicCommand::exec(Irccd &irccd, const nlohmann::json &request) const
-{
-    Command::exec(irccd, request);
-
-    irccd.servers().require(request["server"])->topic(request["channel"], request["topic"]);
-
-    return nlohmann::json::object();
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-server-topic.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * cmd-server-topic.hpp -- 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_CMD_SERVER_TOPIC_HPP
-#define IRCCD_CMD_SERVER_TOPIC_HPP
-
-/**
- * \file cmd-server-topic.hpp
- * \brief Implementation of server-topic transport command.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of server-topic transport command.
- */
-class ServerTopicCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT ServerTopicCommand();
-
-    /**
-     * \copydoc Command::args
-     */
-    IRCCD_EXPORT std::vector<Arg> args() const override;
-
-    /**
-     * \copydoc Command::properties
-     */
-    IRCCD_EXPORT std::vector<Property> properties() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-
-    /**
-     * \copydoc Command::exec
-     */
-    IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_SERVER_TOPIC_HPP
--- a/lib/irccd/cmd-watch.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-/*
- * cmd-watch.cpp -- implementation of irccdctl watch
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <functional>
-#include <iostream>
-#include <sstream>
-#include <unordered_map>
-
-#include "cmd-watch.hpp"
-#include "irccdctl.hpp"
-
-namespace irccd {
-
-namespace command {
-
-namespace {
-
-std::string dump(const nlohmann::json &object, const std::string &property)
-{
-    auto it = object.find(property);
-
-    if (it == object.end())
-        return "";
-
-    return it->dump();
-}
-
-void onChannelMode(const nlohmann::json &v)
-{
-    std::cout << "event:       onChannelMode\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "mode:        " << dump(v, "mode") << "\n";
-    std::cout << "argument:    " << dump(v, "argument") << "\n";
-}
-
-void onChannelNotice(const nlohmann::json &v)
-{
-    std::cout << "event:       onChannelNotice\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "message:     " << dump(v, "message") << "\n";
-}
-
-void onConnect(const nlohmann::json &v)
-{
-    std::cout << "event:       onConnect\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-}
-
-void onInvite(const nlohmann::json &v)
-{
-    std::cout << "event:       onInvite\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-}
-
-void onJoin(const nlohmann::json &v)
-{
-    std::cout << "event:       onJoin\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-}
-
-void onKick(const nlohmann::json &v)
-{
-    std::cout << "event:       onKick\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "target:      " << dump(v, "target") << "\n";
-    std::cout << "reason:      " << dump(v, "reason") << "\n";
-}
-
-void onMessage(const nlohmann::json &v)
-{
-    std::cout << "event:       onMessage\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "message:     " << dump(v, "message") << "\n";
-}
-
-void onMe(const nlohmann::json &v)
-{
-    std::cout << "event:       onMe\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "target:      " << dump(v, "target") << "\n";
-    std::cout << "message:     " << dump(v, "message") << "\n";
-}
-
-void onMode(const nlohmann::json &v)
-{
-    std::cout << "event:       onMode\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "mode:        " << dump(v, "mode") << "\n";
-}
-
-void onNames(const nlohmann::json &v)
-{
-    std::cout << "event:       onNames\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "names:       " << dump(v, "names") << "\n";
-}
-
-void onNick(const nlohmann::json &v)
-{
-    std::cout << "event:       onNick\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "nickname:    " << dump(v, "nickname") << "\n";
-}
-
-void onNotice(const nlohmann::json &v)
-{
-    std::cout << "event:       onNotice\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "message:     " << dump(v, "message") << "\n";
-}
-
-void onPart(const nlohmann::json &v)
-{
-    std::cout << "event:       onPart\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "reason:      " << dump(v, "reason") << "\n";
-}
-
-void onQuery(const nlohmann::json &v)
-{
-    std::cout << "event:       onQuery\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "message:     " << dump(v, "message") << "\n";
-}
-
-void onTopic(const nlohmann::json &v)
-{
-    std::cout << "event:       onTopic\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "origin:      " << dump(v, "origin") << "\n";
-    std::cout << "channel:     " << dump(v, "channel") << "\n";
-    std::cout << "topic:       " << dump(v, "topic") << "\n";
-}
-
-void onWhois(const nlohmann::json &v)
-{
-    std::cout << "event:       onWhois\n";
-    std::cout << "server:      " << dump(v, "server") << "\n";
-    std::cout << "nickname:    " << dump(v, "nickname") << "\n";
-    std::cout << "username:    " << dump(v, "username") << "\n";
-    std::cout << "host:        " << dump(v, "host") << "\n";
-    std::cout << "realname:    " << dump(v, "realname") << "\n";
-}
-
-const std::unordered_map<std::string, std::function<void (const nlohmann::json &)>> 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
-
-WatchCommand::WatchCommand()
-    : Command("watch", "General", "Start watching irccd events")
-{
-}
-
-std::vector<Command::Option> WatchCommand::options() const
-{
-    return {{ "format", "f", "format", "format", "output format" }};
-}
-
-nlohmann::json WatchCommand::request(Irccdctl &ctl, const CommandRequest &request) const
-{
-    std::string format = request.optionOr("format", "native");
-
-    if (format != "native" && format != "json")
-        throw std::invalid_argument("invalid format given: " + format);
-
-    while (ctl.client().isConnected()) {
-        try {
-            auto object = ctl.waitEvent();
-            auto event = object.find("event");
-
-            if (event == object.end() || !event->is_string())
-                continue;
-
-            auto it = events.find(*event);
-
-            // Silently ignore to avoid breaking user output.
-            if (it == events.end())
-                continue;
-
-            if (format == "json")
-                std::cout << object.dump() << std::endl;
-            else {
-                it->second(object);
-                std::cout << std::endl;
-            }
-        } catch (...) {
-        }
-    }
-
-    return nullptr;
-}
-
-} // !command
-
-} // !irccd
--- a/lib/irccd/cmd-watch.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * cmd-watch.hpp -- 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 IRCCD_CMD_WATCH_HPP
-#define IRCCD_CMD_WATCH_HPP
-
-/**
- * \file cmd-watch.hpp
- * \brief Implementation of irccdctl watch.
- */
-
-#include "command.hpp"
-
-namespace irccd {
-
-namespace command {
-
-/**
- * \brief Implementation of irccdctl watch.
- */
-class WatchCommand : public Command {
-public:
-    /**
-     * Constructor.
-     */
-    IRCCD_EXPORT WatchCommand();
-
-    /**
-     * \copydoc Command::options
-     */
-    IRCCD_EXPORT std::vector<Option> options() const override;
-
-    /**
-     * \copydoc Command::request
-     */
-    IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
-};
-
-} // !command
-
-} // !irccd
-
-#endif // !IRCCD_CMD_WATCH_HPP
--- a/lib/irccd/command.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * command.cpp -- remote command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iomanip>
-#include <numeric>
-#include <sstream>
-
-#include <format.h>
-
-#include "command.hpp"
-#include "logger.hpp"
-#include "system.hpp"
-
-using namespace std::string_literals;
-
-using namespace fmt::literals;
-
-using json = nlohmann::json;
-
-namespace irccd {
-
-namespace {
-
-/*
- * typeName
- * ------------------------------------------------------------------
- *
- * Convert a JSON value type to string for convenience.
- */
-std::string typeName(nlohmann::json::value_t type) noexcept
-{
-    switch (type) {
-    case nlohmann::json::value_t::array:
-        return "array";
-    case nlohmann::json::value_t::boolean:
-        return "bool";
-    case nlohmann::json::value_t::number_float:
-        return "float";
-    case nlohmann::json::value_t::number_integer:
-        return "integer";
-    case nlohmann::json::value_t::number_unsigned:
-        return "unsigned";
-    case nlohmann::json::value_t::null:
-        return "null";
-    case nlohmann::json::value_t::object:
-        return "object";
-    case nlohmann::json::value_t::string:
-        return "string";
-    default:
-        return "";
-    }
-}
-
-/*
- * typeNameList
- * ------------------------------------------------------------------
- *
- * Construct a list of names to send a convenient error message if properties
- * are invalid, example: string, int or bool expected.
- */
-
-std::string typeNameList(const std::vector<json::value_t> &types)
-{
-    std::ostringstream oss;
-
-    if (types.size() == 1)
-        return typeName(types[0]);
-
-    for (std::size_t i = 0; i < types.size(); ++i) {
-        oss << typeName(types[i]);
-
-        if (i == types.size() - 2)
-            oss << " or ";
-        else if (i < types.size() - 1)
-            oss << ", ";
-    }
-
-    return oss.str();
-}
-
-} // !namespace
-
-/*
- * JSON errors
- * ------------------------------------------------------------------
- */
-
-MissingPropertyError::MissingPropertyError(std::string name, std::vector<nlohmann::json::value_t> types)
-    : m_name(std::move(name))
-    , m_types(std::move(types))
-{
-    m_message = "missing '" + m_name + "' property (" + typeNameList(m_types) + " expected)";
-}
-
-InvalidPropertyError::InvalidPropertyError(std::string name, nlohmann::json::value_t expected, nlohmann::json::value_t result)
-    : m_name(std::move(name))
-    , m_expected(expected)
-    , m_result(result)
-{
-    m_message += "invalid '" + m_name + "' property ";
-    m_message += "(" + typeName(expected) + " expected, ";
-    m_message += "got " + typeName(result) + ")";
-}
-
-PropertyRangeError::PropertyRangeError(std::string name, std::uint64_t min, std::uint64_t max, std::uint64_t value)
-    : m_name(std::move(name))
-    , m_min(min)
-    , m_max(max)
-    , m_value(value)
-{
-    assert(value < min || value > max);
-
-    m_message += "property '" + m_name + "' is out of range ";
-    m_message += std::to_string(min) + ".." + std::to_string(max) + ", got " + std::to_string(value);
-}
-
-PropertyError::PropertyError(std::string name, std::string message)
-    : m_name(std::move(name))
-{
-    m_message += "property '" + m_name + "': " + message;
-}
-
-/*
- * Command implementation
- * ------------------------------------------------------------------
- */
-
-std::string Command::usage() const
-{
-    std::ostringstream oss;
-
-    oss << m_name << " ";
-
-    // Options.
-    auto optlist = options();
-
-    if (optlist.size() > 0) {
-        for (const auto &opt : optlist) {
-            oss << "[";
-
-            /*
-             * Long options are too big so only show them in the help
-             * command usage or only if no short option is available.
-             */
-            if (opt.simpleKey().size() > 0)
-                oss << "-" << opt.simpleKey();
-            else if (opt.longKey().size() > 0)
-                oss << " --" << opt.longKey();
-
-            oss << (opt.arg().empty() ? "" : " ") << opt.arg() << "] ";
-        }
-    }
-
-    // Arguments.
-    auto argslist = args();
-
-    if (argslist.size() > 0) {
-        for (const auto &arg : argslist)
-            oss << (arg.required() ? "" : "[")
-                << arg.name()
-                << (arg.required() ? "" : "]") << " ";
-    }
-
-    return oss.str();
-}
-
-std::string Command::help() const
-{
-    std::ostringstream oss;
-
-    oss << "usage: " << sys::programName() << " " << m_name;
-
-    // Options summary.
-    if (options().size() > 0)
-        oss << " [options...]";
-
-    // Arguments summary.
-    if (args().size() > 0) {
-        oss << " ";
-
-        for (const auto &arg : args())
-            oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " ";
-    }
-
-    // Description.
-    oss << "\n\n" << m_description << "\n\n";
-
-    // Options.
-    if (options().size() > 0) {
-        oss << "Options:\n";
-
-        for (const auto &opt : options()) {
-            std::ostringstream optoss;
-
-            // Construct the line for the option in a single string to pad it correctly.
-            optoss << "  ";
-            optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : "   ");
-            optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : "");
-            optoss << opt.arg();
-
-            // Add it padded with spaces.
-            oss << std::left << std::setw(28) << optoss.str();
-            oss << opt.description() << "\n";
-        }
-    }
-
-    return oss.str();
-}
-
-unsigned Command::min() const noexcept
-{
-    auto list = args();
-
-    return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned {
-        return i + (arg.required() ? 1 : 0);
-    });
-}
-
-unsigned Command::max() const noexcept
-{
-    return (unsigned)args().size();
-}
-
-nlohmann::json Command::request(Irccdctl &, const CommandRequest &) const
-{
-    return nlohmann::json::object({});
-}
-
-nlohmann::json Command::exec(Irccd &, const nlohmann::json &request) const
-{
-    // Verify that requested properties are present in the request.
-    for (const auto &prop : properties()) {
-        auto it = request.find(prop.name());
-
-        if (it == request.end())
-            throw std::invalid_argument("missing '{}' property"_format(prop.name()));
-
-        if (std::find(prop.types().begin(), prop.types().end(), it->type()) == prop.types().end()) {
-            auto expected = typeNameList(prop.types());
-            auto got = typeName(it->type());
-
-            throw std::invalid_argument("invalid '{}' property ({} expected, got {})"_format(prop.name(), expected, got));
-        }
-    }
-
-    return nlohmann::json::object({});
-}
-
-void Command::result(Irccdctl &, const nlohmann::json &response) const
-{
-    auto it = response.find("error");
-
-    if (it != response.end() && it->is_string())
-        log::warning() << "irccdctl: " << it->dump() << std::endl;
-}
-
-} // !irccd
--- a/lib/irccd/command.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,709 +0,0 @@
-/*
- * command.hpp -- remote command
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_COMMAND_HPP
-#define IRCCD_COMMAND_HPP
-
-/**
- * \file command.hpp
- * \brief Remote commands.
- */
-
-#include <cassert>
-#include <map>
-#include <vector>
-
-#include "json.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class Irccd;
-class Irccdctl;
-
-/**
- * \brief A JSON property is missing.
- */
-class MissingPropertyError : public std::exception {
-private:
-    std::string m_message;
-    std::string m_name;
-    std::vector<nlohmann::json::value_t> m_types;
-
-public:
-    /**
-     * Constructor.
-     */
-    MissingPropertyError(std::string name, std::vector<nlohmann::json::value_t> types);
-
-    /**
-     * Get human error message.
-     *
-     * \return a message
-     */
-    const char *what() const noexcept override
-    {
-        return m_message.c_str();
-    }
-};
-
-/**
- * \brief A JSON property is invalid
- */
-class InvalidPropertyError : public std::exception {
-private:
-    std::string m_message;
-    std::string m_name;
-
-    nlohmann::json::value_t m_expected;
-    nlohmann::json::value_t m_result;
-
-public:
-    /**
-     * Constructor.
-     *
-     * \param name the property name
-     * \param expected the expected type
-     * \param result the type received
-     */
-    InvalidPropertyError(std::string name, nlohmann::json::value_t expected, nlohmann::json::value_t result);
-
-    /**
-     * Get human error message.
-     *
-     * \return a message
-     */
-    const char *what() const noexcept override
-    {
-        return m_message.c_str();
-    }
-};
-
-/**
- * \brief Property range error.
- */
-class PropertyRangeError : public std::exception {
-private:
-    std::string m_message;
-    std::string m_name;
-    std::uint64_t m_min;
-    std::uint64_t m_max;
-    std::uint64_t m_value;
-
-public:
-    /**
-     * Constructor.
-     *
-     * \pre value < min || value > max
-     * \param name the property name
-     * \param min the minimum value
-     * \param max the maximum value
-     * \param value the actual value
-     */
-    PropertyRangeError(std::string name, std::uint64_t min, std::uint64_t max, std::uint64_t value);
-
-    /**
-     * Get human error message.
-     *
-     * \return a message
-     */
-    const char *what() const noexcept override
-    {
-        return m_message.c_str();
-    }
-};
-
-/**
- * \brief Generic error for JSON properties.
- */
-class PropertyError : public std::exception {
-private:
-    std::string m_message;
-    std::string m_name;
-
-public:
-    /**
-     * Constructor.
-     *
-     * \param name the property name
-     * \param message the error message
-     */
-    PropertyError(std::string name, std::string message);
-
-    /**
-     * Get human error message.
-     *
-     * \return a message
-     */
-    const char *what() const noexcept override
-    {
-        return m_message.c_str();
-    }
-};
-
-
-/**
- * \brief Namespace for remote commands.
- */
-//namespace command {
-
-/**
- * \brief Command line arguments to irccdctl.
- *
- * This class contains the resolved arguments from command line that can apply
- * to the command.
- */
-class CommandRequest {
-public:
-    /**
-     * The options given by command line.
-     */
-    using Options = std::multimap<std::string, std::string>;
-
-    /**
-     * Command line arguments in the same order.
-     */
-    using Args = std::vector<std::string>;
-
-private:
-    Options m_options;
-    Args m_args;
-
-public:
-    /**
-     * Construct the request.
-     *
-     * \param options the options
-     * \param args the arguments
-     */
-    inline CommandRequest(Options options, Args args) noexcept
-        : m_options(std::move(options))
-        , m_args(std::move(args))
-    {
-    }
-
-    /**
-     * Get the arguments.
-     *
-     * \return the arguments
-     */
-    inline const Args &args() const noexcept
-    {
-        return m_args;
-    }
-
-    /**
-     * Get the options.
-     *
-     * \return the options
-     */
-    inline const Options &options() const noexcept
-    {
-        return m_options;
-    }
-
-    /**
-     * Get the number of arguments.
-     *
-     * \return the number of arguments
-     */
-    inline unsigned length() const noexcept
-    {
-        return (unsigned)m_args.size();
-    }
-
-    /**
-     * Check if the request has the given option id.
-     *
-     * \param option the option id
-     * \return true if the option is available
-     */
-    inline bool has(const std::string &option) const noexcept
-    {
-        return m_options.count(option) != 0;
-    }
-
-    /**
-     * Get the argument at the specified index.
-     *
-     * \pre index < length()
-     * \param index the argument index
-     * \return the argument
-     */
-    inline const std::string &arg(unsigned index) const noexcept
-    {
-        assert(index < m_args.size());
-
-        return m_args[index];
-    }
-
-    /**
-     * Get the argument or default value if not available.
-     *
-     * \param index the index
-     * \param defaultValue the value if index is out of range
-     * \return the argument
-     */
-    inline std::string argOr(unsigned index, std::string defaultValue) const noexcept
-    {
-        return index < m_args.size() ? m_args[index] : defaultValue;
-    }
-
-    /**
-     * Get the given option by its id.
-     *
-     * \pre has(key)
-     * \param key the option id
-     * \return the option
-     */
-    inline const std::string &option(const std::string &key) const noexcept
-    {
-        assert(m_options.count(key) != 0);
-
-        return m_options.find(key)->second;
-    }
-
-    /**
-     * Get the given option by its id or defaultValue if not found.
-     *
-     * \param key the option id
-     * \param defaultValue the value replacement
-     * \return the option
-     */
-    inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept
-    {
-        auto it = m_options.find(key);
-
-        if (it == m_options.end())
-            return defaultValue;
-
-        return it->second;
-    }
-};
-
-/**
- * \brief Invokable command.
- *
- * A remote command is a invokable command in the irccd daemon. You can register
- * dynamically any remote command you like using Application::addCommand.
- *
- * The remote command will be usable directly from irccdctl without any other
- * code.
- *
- * A remote command can have options and arguments. Options always come first,
- * before arguments.
- *
- * The command workflow is defined as follow:
- *
- * 1. User wants to invoke a command, request() is called and return a JSON
- *    object containaing the request, it it send to the daemon.
- *
- * 2. The daemon receive the request and execute it using exec(). It returns a
- *    JSON object containint the request result or error if any.
- *
- * 3. Finally, the command receives the result in result() function and user can
- *    manipulate it. For convenience, the default implementation shows the error
- *    if any.
- */
-class Command {
-public:
-    /**
-     * \brief Defines available options for this command.
-     */
-    class Option;
-
-    /**
-     * \brief Defines available arguments for this command.
-     */
-    class Arg;
-
-    /**
-     * \brief Defines properties that must be available in the JSON request.
-     */
-    class Property;
-
-private:
-    std::string m_name;
-    std::string m_category;
-    std::string m_description;
-    bool m_visible;
-
-public:
-    /**
-     * Create the remote command.
-     *
-     * \pre name must not be empty
-     * \pre category must not be empty
-     * \param name the command name (e.g. server-list)
-     * \param category the category (e.g. Server)
-     * \param description a one line description with no dots, no new line
-     * \param visible true if the command should be visible without verbosity
-     */
-    inline Command(std::string name,
-                   std::string category,
-                   std::string description,
-                   bool visible = true) noexcept
-        : m_name(std::move(name))
-        , m_category(std::move(category))
-        , m_description(std::move(description))
-        , m_visible(visible)
-    {
-        assert(!m_name.empty());
-        assert(!m_category.empty());
-    }
-
-    /**
-     * Default destructor virtual.
-     */
-    virtual ~Command() = 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;
-    }
-
-    /**
-     * Get the command description.
-     *
-     * \return the description
-     */
-    inline const std::string &description() const noexcept
-    {
-        return m_description;
-    }
-
-    /**
-     * Hide the command in non-verbose mode.
-     *
-     * \return true if the command should be visible in non-verbose mode
-     */
-    inline bool visible() const noexcept
-    {
-        return m_visible;
-    }
-
-    /**
-     * Return the command documentation usage.
-     *
-     * \return the usage
-     */
-    IRCCD_EXPORT std::string usage() const;
-
-    /**
-     * Return the help message.
-     *
-     * \return the help message
-     */
-    IRCCD_EXPORT std::string help() const;
-
-    /**
-     * Get the supported irccdctl options.
-     *
-     * \return the options
-     */
-    virtual std::vector<Option> options() const
-    {
-        return {};
-    }
-
-    /**
-     * Get the supported arguments.
-     *
-     * \return the arguments
-     */
-    virtual std::vector<Arg> args() const
-    {
-        return {};
-    }
-
-    /**
-     * Get the properties required in the JSON request.
-     *
-     * Default implementation returns empty list.
-     *
-     * \return the required properties
-     * \note Put only **required** properties
-     */
-    virtual std::vector<Property> properties() const
-    {
-        return {};
-    }
-
-    /**
-     * Get the minimum number of arguments required.
-     *
-     * \return the minimum
-     */
-    IRCCD_EXPORT unsigned min() const noexcept;
-
-    /**
-     * Get the maximum number of arguments required.
-     *
-     * \return the maximum
-     */
-    IRCCD_EXPORT unsigned max() const noexcept;
-
-    /**
-     * Prepare a JSON request to the daemon.
-     *
-     * If the command is local and does not need to send anything to irccd's
-     * instance, return a null JSON value.
-     *
-     * The default implementation just send the command name with no arguments.
-     *
-     * \param irccdctl the irccdctl instance
-     * \param args the command line arguments and options
-     * \return the JSON object to send to the daemon
-     * \post the returned JSON value must be an object
-     */
-    IRCCD_EXPORT virtual nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &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
-     */
-    IRCCD_EXPORT virtual nlohmann::json exec(Irccd &irccd, const nlohmann::json &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 instance
-     * \param response the JSON response
-     */
-    IRCCD_EXPORT virtual void result(Irccdctl &irccdctl, const nlohmann::json &response) const;
-};
-
-/**
- * \brief Option description for a command.
- */
-class Command::Option {
-private:
-    std::string m_id;
-    std::string m_simple;
-    std::string m_long;
-    std::string m_arg;
-    std::string m_description;
-
-public:
-    /**
-     * Constructor an option description.
-     *
-     * Simple and long keys must not start with '-' or '--', they will be added
-     * automatically.
-     *
-     * If arg is not empty, the option takes an argument.
-     *
-     * \pre id must not be empty
-     * \pre at least simpleKey or longKey must not be empty
-     * \pre description must not be empty
-     * \param id the option id
-     * \param simpleKey the key the option key
-     * \param longKey the long option name
-     * \param arg the argument name if needed
-     * \param description the description
-     */
-    inline Option(std::string id,
-              std::string simpleKey,
-              std::string longKey,
-              std::string arg,
-              std::string description) noexcept
-        : m_id(std::move(id))
-        , m_simple(std::move(simpleKey))
-        , m_long(std::move(longKey))
-        , m_arg(std::move(arg))
-        , m_description(std::move(description))
-    {
-        assert(!m_id.empty());
-        assert(!m_simple.empty() || !m_long.empty());
-        assert(!m_description.empty());
-    }
-
-    /**
-     * Get the id.
-     *
-     * \return the id
-     */
-    inline const std::string &id() const noexcept
-    {
-        return m_id;
-    }
-
-    /**
-     * Get the option key.
-     *
-     * \return the key
-     */
-    inline const std::string &simpleKey() const noexcept
-    {
-        return m_simple;
-    }
-
-    /**
-     * Get the long option.
-     *
-     * \return the long option
-     */
-    inline const std::string &longKey() const noexcept
-    {
-        return m_long;
-    }
-
-    /**
-     * Get the option description.
-     *
-     * \return the description
-     */
-    inline const std::string &description() const noexcept
-    {
-        return m_description;
-    }
-
-    /**
-     * Get the option argument name.
-     *
-     * \return the argument name if any
-     */
-    inline const std::string &arg() const noexcept
-    {
-        return m_arg;
-    }
-};
-
-/**
- * \brief Argument description for command.
- */
-class Command::Arg {
-private:
-    std::string m_name;
-    bool m_required;
-
-public:
-    /**
-     * Construct an argument.
-     *
-     * \param name the name
-     * \param required true if the argument is required
-     */
-    inline Arg(std::string name, bool required) noexcept
-        : m_name(std::move(name))
-        , m_required(required)
-    {
-    }
-
-    /**
-     * Get the argument name.
-     *
-     * \return the name
-     */
-    inline const std::string &name() const noexcept
-    {
-        return m_name;
-    }
-
-    /**
-     * Tells if the argument is required.
-     *
-     * \return true if required
-     */
-    inline bool required() const noexcept
-    {
-        return m_required;
-    }
-};
-
-/**
- * \brief Property description for JSON request.
- */
-class Command::Property {
-private:
-    std::string m_name;
-    std::vector<nlohmann::json::value_t> m_types;
-
-public:
-    /**
-     * Construct the property description.
-     *
-     * \pre !name.empty()
-     * \pre types.size() >= 1
-     * \param name the name
-     * \param types the json types allowed
-     */
-    inline Property(std::string name, std::vector<nlohmann::json::value_t> types = { nlohmann::json::value_t::string }) noexcept
-        : m_name(std::move(name))
-        , m_types(std::move(types))
-    {
-        assert(!m_name.empty());
-        assert(m_types.size() >= 1);
-    }
-
-    /**
-     * Get the property name.
-     *
-     * \return the name
-     */
-    inline const std::string &name() const noexcept
-    {
-        return m_name;
-    }
-
-    /**
-     * Get the property types.
-     *
-     * \return the types
-     */
-    inline const std::vector<nlohmann::json::value_t> &types() const noexcept
-    {
-        return m_types;
-    }
-};
-
-} // !irccd
-
-#endif // !IRCCD_COMMAND_HPP
--- a/lib/irccd/config.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,568 +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 <cassert>
-
-#include <format.h>
-
-#include "config.hpp"
-#include "fs.hpp"
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "path.hpp"
-#include "plugin-js.hpp"
-#include "rule.hpp"
-#include "server.hpp"
-#include "service-plugin.hpp"
-#include "sysconfig.hpp"
-#include "transport.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-namespace {
-
-class IrccdLogFilter : public log::Filter {
-private:
-    std::string convert(const std::string &tmpl, std::string input) const
-    {
-        if (tmpl.empty())
-            return input;
-
-        util::Substitution params;
-
-        params.flags &= ~(util::Substitution::IrcAttrs);
-        params.keywords.emplace("message", std::move(input));
-
-        return util::format(tmpl, params);
-    }
-
-public:
-    std::string m_debug;
-    std::string m_info;
-    std::string m_warning;
-
-    std::string preDebug(std::string input) const override
-    {
-        return convert(m_debug, std::move(input));
-    }
-
-    std::string preInfo(std::string input) const override
-    {
-        return convert(m_info, std::move(input));
-    }
-
-    std::string preWarning(std::string input) const override
-    {
-        return convert(m_warning, std::move(input));
-    }
-};
-
-std::string get(const ini::Document &doc, const std::string &section, const std::string &key)
-{
-    auto its = doc.find(section);
-
-    if (its == doc.end())
-        return "";
-
-    auto ito = its->find(key);
-
-    if (ito == its->end())
-        return "";
-
-    return ito->value();
-}
-
-PluginConfig loadPluginConfig(const ini::Section &sc)
-{
-    PluginConfig config;
-
-    for (const auto &option : sc)
-        config.emplace(option.key(), option.value());
-
-    return config;
-}
-
-std::unique_ptr<log::Logger> loadLogFile(const ini::Section &sc)
-{
-    /*
-     * TODO: improve that with CMake options.
-     */
-#if defined(IRCCD_SYSTEM_WINDOWS)
-    std::string normal = "log.txt";
-    std::string errors = "errors.txt";
-#else
-    std::string normal = "/var/log/irccd/log.txt";
-    std::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();
-
-    return std::make_unique<log::FileLogger>(std::move(normal), std::move(errors));
-}
-
-std::unique_ptr<log::Logger> loadLogSyslog()
-{
-#if defined(HAVE_SYSLOG)
-    return std::make_unique<log::SyslogLogger>();
-#else
-    throw std::runtime_error("logs: syslog is not available on this platform");
-#endif // !HAVE_SYSLOG
-}
-
-std::shared_ptr<TransportServer> loadTransportIp(const ini::Section &sc)
-{
-    assert(sc.key() == "transport");
-
-    std::shared_ptr<TransportServer> transport;
-    ini::Section::const_iterator it;
-
-    // Port.
-    int port;
-
-    if ((it = sc.find("port")) == sc.cend())
-        throw std::invalid_argument("transport: missing 'port' parameter");
-
-    try {
-        port = util::toNumber<std::uint16_t>(it->value());
-    } catch (const std::exception &) {
-        throw std::invalid_argument("transport: invalid port number: {}"_format(it->value()));
-    }
-
-    // Address.
-    std::string address = "*";
-
-    if ((it = sc.find("address")) != sc.end())
-        address = it->value();
-
-    // Domain
-    std::uint8_t mode = TransportServerIp::v4;
-
-    if ((it = sc.find("domain")) != sc.end()) {
-        mode = 0;
-
-        for (const auto &v : *it) {
-            if (v == "ipv4")
-                mode |= TransportServerIp::v4;
-            if (v == "ipv6")
-                mode |= TransportServerIp::v6;
-        }
-    }
-
-    // Optional SSL.
-    std::string pkey;
-    std::string cert;
-
-    if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value())) {
-        if ((it = sc.find("certificate")) == sc.end())
-            throw std::invalid_argument("transport: missing 'certificate' parameter");
-
-        cert = it->value();
-
-        if ((it = sc.find("key")) == sc.end())
-            throw std::invalid_argument("transport: missing 'key' parameter");
-
-        pkey = it->value();
-    }
-
-    if (mode == 0)
-        throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6");
-
-    if (pkey.empty())
-        return std::make_shared<TransportServerIp>(address, port, mode);
-
-    return std::make_shared<TransportServerTls>(pkey, cert, address, port, mode);
-}
-
-std::shared_ptr<TransportServer> loadTransportUnix(const ini::Section &sc)
-{
-    assert(sc.key() == "transport");
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-    ini::Section::const_iterator it = sc.find("path");
-
-    if (it == sc.end())
-        throw std::invalid_argument("transport: missing 'path' parameter");
-
-    return std::make_shared<TransportServerLocal>(it->value());
-#else
-    (void)sc;
-
-    throw std::invalid_argument("transport: unix transport not supported on on this platform");
-#endif
-}
-
-std::shared_ptr<TransportServer> loadTransport(const ini::Section &sc)
-{
-    assert(sc.key() == "transport");
-
-    std::shared_ptr<TransportServer> transport;
-    ini::Section::const_iterator it = sc.find("type");
-
-    if (it == sc.end())
-        throw std::invalid_argument("transport: missing 'type' parameter");
-
-    if (it->value() == "ip")
-        transport = loadTransportIp(sc);
-    else if (it->value() == "unix")
-        transport = loadTransportUnix(sc);
-    else
-        throw std::invalid_argument("transport: invalid type given: {}"_format(it->value()));
-
-    if ((it = sc.find("password")) != sc.end())
-        transport->setPassword(it->value());
-
-    return transport;
-}
-
-Rule loadRule(const ini::Section &sc)
-{
-    assert(sc.key() == "rule");
-
-    // 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("rule: missing 'action'' parameter");
-
-    if (it->value() == "drop")
-        action = RuleAction::Drop;
-    else if (it->value() == "accept")
-        action = RuleAction::Accept;
-    else
-        throw std::invalid_argument("rule: invalid action given: {}"_format(it->value()));
-
-    return Rule(std::move(servers),
-            std::move(channels),
-            std::move(origins),
-            std::move(plugins),
-            std::move(events),
-            action);
-}
-
-std::shared_ptr<Server> loadServer(const ini::Section &sc, const Config &config)
-{
-    assert(sc.key() == "server");
-
-    // Name.
-    ini::Section::const_iterator it;
-
-    if ((it = sc.find("name")) == sc.end())
-        throw std::invalid_argument("server: missing 'name' parameter");
-    else if (!util::isIdentifierValid(it->value()))
-        throw std::invalid_argument("server: invalid identifier: {}"_format(it->value()));
-
-    auto server = std::make_shared<Server>(it->value());
-
-    // Host
-    if ((it = sc.find("host")) == sc.end())
-        throw std::invalid_argument("server {}: missing host"_format(server->name()));
-
-    server->setHost(it->value());
-
-    // Optional password
-    if ((it = sc.find("password")) != sc.end())
-        server->setPassword(it->value());
-
-    // Optional flags
-    if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value()))
-        server->setFlags(server->flags() | Server::Ipv6);
-    if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value()))
-        server->setFlags(server->flags() | Server::Ssl);
-    if ((it = sc.find("ssl-verify")) != sc.end() && util::isBoolean(it->value()))
-        server->setFlags(server->flags() | Server::SslVerify);
-
-    // Optional identity
-    if ((it = sc.find("identity")) != sc.end())
-        config.loadServerIdentity(*server, it->value());
-
-    // Options
-    if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value()))
-        server->setFlags(server->flags() | Server::AutoRejoin);
-    if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value()))
-        server->setFlags(server->flags() | Server::JoinInvite);
-
-    // Channels
-    if ((it = sc.find("channels")) != sc.end()) {
-        for (const std::string &s : *it) {
-            Channel 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;
-
-            //server.channels.push_back(std::move(channel));
-            //server->join()
-            server->join(channel.name, channel.password);
-        }
-    }
-    if ((it = sc.find("command-char")) != sc.end())
-        server->setCommandCharacter(it->value());
-
-    // Reconnect and ping timeout
-    try {
-        if ((it = sc.find("port")) != sc.end())
-            server->setPort(util::toNumber<std::uint16_t>(it->value()));
-        if ((it = sc.find("reconnect-tries")) != sc.end())
-            server->setReconnectTries(util::toNumber<std::int8_t>(it->value()));
-        if ((it = sc.find("reconnect-timeout")) != sc.end())
-            server->setReconnectDelay(util::toNumber<std::uint16_t>(it->value()));
-        if ((it = sc.find("ping-timeout")) != sc.end())
-            server->setPingTimeout(util::toNumber<std::uint16_t>(it->value()));
-    } catch (const std::exception &) {
-        log::warning("server {}: invalid number for {}: {}"_format(server->name(), it->key(), it->value()));
-    }
-
-    return server;
-}
-
-} // !namespace
-
-Config Config::find()
-{
-    for (const auto &path : path::list(path::PathConfig)) {
-        std::string fullpath = path + "irccd.conf";
-
-        if (!fs::isReadable(fullpath))
-            continue;
-
-        try {
-            return Config(fullpath);
-        } catch (const std::exception &ex) {
-            throw std::runtime_error("{}: {}"_format(fullpath, ex.what()));
-        }
-    }
-
-    throw std::runtime_error("no configuration file found");
-}
-
-void Config::loadServerIdentity(Server &server, const std::string &identity) const
-{
-    ini::Document::const_iterator sc = std::find_if(m_document.begin(), m_document.end(), [&] (const auto &sc) {
-        if (sc.key() != "identity")
-            return false;
-
-        auto name = sc.find("name");
-
-        return name != sc.end() && name->value() == identity;
-    });
-
-    if (sc == m_document.end())
-        return;
-
-    ini::Section::const_iterator it;
-
-    if ((it = sc->find("username")) != sc->end())
-        server.setUsername(it->value());
-    if ((it = sc->find("realname")) != sc->end())
-        server.setRealname(it->value());
-    if ((it = sc->find("nickname")) != sc->end())
-        server.setNickname(it->value());
-    if ((it = sc->find("ctcp-version")) != sc->end())
-        server.setCtcpVersion(it->value());
-}
-
-PluginConfig Config::findPluginConfig(const std::string &name) const
-{
-    assert(util::isIdentifierValid(name));
-
-    std::string fullname = std::string("plugin.") + name;
-
-    for (const auto &section : m_document) {
-        if (section.key() != fullname)
-            continue;
-
-        return loadPluginConfig(section);
-    }
-
-    return PluginConfig();
-}
-
-PluginFormats Config::findPluginFormats(const std::string &name) const
-{
-    assert(util::isIdentifierValid(name));
-
-    auto section = m_document.find(std::string("format.") + name);
-
-    if (section == m_document.end())
-        return PluginFormats();
-
-    PluginFormats formats;
-
-    for (const auto &opt : *section)
-        formats.emplace(opt.key(), opt.value());
-
-    return formats;
-}
-
-bool Config::isVerbose() const noexcept
-{
-    return util::isBoolean(get(m_document, "logs", "verbose"));
-}
-
-bool Config::isForeground() const noexcept
-{
-    return util::isBoolean(get(m_document, "general", "foreground"));
-}
-
-std::string Config::pidfile() const
-{
-    return get(m_document, "general", "pidfile");
-}
-
-std::string Config::uid() const
-{
-    return get(m_document, "general", "uid");
-}
-
-std::string Config::gid() const
-{
-    return get(m_document, "general", "gid");
-}
-
-void Config::loadLogs() const
-{
-    ini::Document::const_iterator sc = m_document.find("logs");
-
-    if (sc == m_document.end())
-        return;
-
-    ini::Section::const_iterator it;
-
-    if ((it = sc->find("type")) != sc->end()) {
-        std::unique_ptr<log::Logger> iface;
-
-        // Console is the default, no test case.
-        if (it->value() == "file")
-            iface = loadLogFile(*sc);
-        else if (it->value() == "syslog")
-            iface = loadLogSyslog();
-        else
-            throw std::runtime_error("logs: unknown log type: {}"_format(it->value()));
-
-        if (iface)
-            log::setLogger(std::move(iface));
-    }
-}
-
-void Config::loadFormats() const
-{
-    ini::Document::const_iterator sc = m_document.find("format");
-
-    if (sc == m_document.end())
-        return;
-
-    std::unique_ptr<IrccdLogFilter> filter = std::make_unique<IrccdLogFilter>();
-    ini::Section::const_iterator it;
-
-    if ((it = sc->find("debug")) != sc->cend())
-        filter->m_debug = it->value();
-    if ((it = sc->find("info")) != sc->cend())
-        filter->m_info = it->value();
-    if ((it = sc->find("warning")) != sc->cend())
-        filter->m_warning = it->value();
-
-    log::setFilter(std::move(filter));
-}
-
-std::vector<std::shared_ptr<TransportServer>> Config::loadTransports() const
-{
-    std::vector<std::shared_ptr<TransportServer>> transports;
-
-    for (const auto &section : m_document)
-        if (section.key() == "transport")
-            transports.push_back(loadTransport(section));
-
-    return transports;
-}
-
-std::vector<Rule> Config::loadRules() const
-{
-    std::vector<Rule> rules;
-
-    for (const auto &section : m_document)
-        if (section.key() == "rule")
-            rules.push_back(loadRule(section));
-
-    return rules;
-}
-
-std::vector<std::shared_ptr<Server>> Config::loadServers() const
-{
-    std::vector<std::shared_ptr<Server>> servers;
-
-    for (const auto &section : m_document) {
-        if (section.key() != "server")
-            continue;
-
-        try {
-            servers.push_back(loadServer(section, *this));
-        } catch (const std::exception &ex) {
-            log::warning(ex.what());
-        }
-    }
-
-    return servers;
-}
-
-void Config::loadPlugins(Irccd &irccd) const
-{
-    auto it = m_document.find("plugins");
-
-    if (it != m_document.end()) {
-        for (const auto &option : *it) {
-            if (!util::isIdentifierValid(option.key()))
-                continue;
-
-            irccd.plugins().setConfig(option.key(), findPluginConfig(option.key()));
-            irccd.plugins().setFormats(option.key(), findPluginFormats(option.key()));
-            irccd.plugins().load(option.key(), option.value());
-        }
-    }
-}
-
-} // !irccd
--- a/lib/irccd/config.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * config.hpp -- 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_HPP
-#define IRCCD_CONFIG_HPP
-
-/**
- * \file config.hpp
- * \brief Read .ini configuration file for irccd
- */
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "ini.hpp"
-#include "plugin.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class Irccd;
-class Rule;
-class Server;
-class TransportServer;
-
-/**
- * \class Config
- * \brief Read .ini configuration file for irccd
- */
-class Config {
-private:
-    std::string m_path;
-    ini::Document m_document;
-
-public:
-    /**
-     * Search the configuration file into the standard defined paths.
-     *
-     * \return the config
-     * \throw std::exception on errors or if no config could be found
-     */
-    IRCCD_EXPORT static Config find();
-
-    /**
-     * Load the configuration from the specified path.
-     *
-     * \param path the path
-     */
-    inline Config(std::string path)
-        : m_path(std::move(path))
-        , m_document(ini::readFile(m_path))
-    {
-    }
-
-    /**
-     * Get the path to the configuration file.
-     *
-     * \return the path
-     */
-    inline const std::string &path() const noexcept
-    {
-        return m_path;
-    }
-
-   /**
-     * Find an entity if defined in the configuration file.
-     *
-     * \pre util::isValidIdentifier(name)
-     * \param server the server to update
-     * \param name the identity name
-     * \return default identity if cannot be found
-     */
-    IRCCD_EXPORT void loadServerIdentity(Server &server, const std::string &name) const;
-
-     /**
-     * Find a plugin configuration if defined in the configuration file.
-     *
-     * \pre util::isValidIdentifier(name)
-     * \return the configuration or empty if not found
-     */
-    IRCCD_EXPORT PluginConfig findPluginConfig(const std::string &name) const;
-
-    /**
-     * Find plugin formats if defined.
-     *
-     * \pre util::isValidIdentifier(name)
-     * \return the formats or empty one if not found
-     */
-    IRCCD_EXPORT PluginFormats findPluginFormats(const std::string &name) const;
-
-    /**
-     * Get the path to the pidfile.
-     *
-     * \return the path or empty if not defined
-     */
-    IRCCD_EXPORT std::string pidfile() const;
-
-    /**
-     * Get the uid.
-     *
-     * \return the uid or empty one if no one is set
-     */
-    IRCCD_EXPORT std::string uid() const;
-
-    /**
-     * Get the gid.
-     *
-     * \return the gid or empty one if no one is set
-     */
-    IRCCD_EXPORT std::string gid() const;
-
-    /**
-     * Check if verbosity is enabled.
-     *
-     * \return true if verbosity was requested
-     */
-    IRCCD_EXPORT bool isVerbose() const noexcept;
-
-    /**
-     * Check if foreground is specified (= no daemonize).
-     *
-     * \return true if foreground was requested
-     */
-    IRCCD_EXPORT bool isForeground() const noexcept;
-
-    /**
-     * Load logging interface.
-     */
-    IRCCD_EXPORT void loadLogs() const;
-
-    /**
-     * Load formats for logging.
-     */
-    IRCCD_EXPORT void loadFormats() const;
-
-    /**
-     * Load transports.
-     *
-     * \return the set of transports
-     */
-    IRCCD_EXPORT std::vector<std::shared_ptr<TransportServer>> loadTransports() const;
-
-    /**
-     * Load rules.
-     *
-     * \return the rules
-     */
-    IRCCD_EXPORT std::vector<Rule> loadRules() const;
-
-    /**
-     * Get the list of servers defined.
-     *
-     * \return the list of servers
-     */
-    IRCCD_EXPORT std::vector<std::shared_ptr<Server>> loadServers() const;
-
-    /**
-     * Get the list of defined plugins.
-     *
-     * \param irccd the irccd instance
-     * \return the list of plugins
-     */
-    IRCCD_EXPORT void loadPlugins(Irccd &irccd) const;
-};
-
-} // !irccd
-
-#endif // !IRCCD_CONFIG_HPP
--- a/lib/irccd/duktape.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,484 +0,0 @@
-/*
- * duktape.hpp -- Duktape extras
- *
- * Copyright (c) 2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_DUKTAPE_HPP
-#define IRCCD_DUKTAPE_HPP
-
-/**
- * \file duktape.hpp
- * \brief Bring some extras to Duktape C library.
- * \author David Demelier <markand@malikania.fr>
- */
-
-#include <cstdio>
-#include <cstdlib>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <duktape.h>
-
-namespace irccd {
-
-/**
- * \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:
-    duk_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(duk_context *ctx, unsigned expected = 0) noexcept
-#if !defined(NDEBUG)
-        : m_context(ctx)
-        , m_expected(expected)
-        , m_begin(static_cast<unsigned>(duk_get_top(ctx)))
-#endif
-    {
-#if defined(NDEBUG)
-        (void)ctx;
-        (void)expected;
-#endif
-    }
-
-    /**
-     * Verify the expected size.
-     *
-     * No-op if NDEBUG is set.
-     */
-    inline ~StackAssert() noexcept
-    {
-#if !defined(NDEBUG)
-        if (static_cast<unsigned>(duk_get_top(m_context)) - m_begin != m_expected) {
-            std::fprintf(stderr, "Corrupt stack detection in StackAssert:\n");
-            std::fprintf(stderr, "  Size at start:            %u\n", m_begin);
-            std::fprintf(stderr, "  Size at end:              %d\n", duk_get_top(m_context));
-            std::fprintf(stderr, "  Expected (user):          %u\n", m_expected);
-            std::fprintf(stderr, "  Expected (adjusted):      %u\n", m_expected + m_begin);
-            std::fprintf(stderr, "  Number of stale values:   %u\n", duk_get_top(m_context) - m_begin - m_expected);
-            std::abort();
-        }
-#endif
-    }
-};
-
-/**
- * \class Exception
- * \brief Error description.
- *
- * This class fills the fields got in an Error object.
- */
-class Exception : 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();
-    }
-};
-
-/**
- * \brief RAII based Duktape handler.
- *
- * This class is implicitly convertible to duk_context for convenience.
- */
-class UniqueContext {
-private:
-    using Deleter = void (*)(duk_context *);
-    using Handle = std::unique_ptr<duk_context, Deleter>;
-
-    Handle m_handle;
-
-    UniqueContext(const UniqueContext &) = delete;
-    UniqueContext &operator=(const UniqueContext &) = delete;
-
-public:
-    /**
-     * Create default context.
-     */
-    inline UniqueContext()
-        : m_handle(duk_create_heap_default(), duk_destroy_heap)
-    {
-    }
-
-    /**
-     * Default move constructor.
-     */
-    UniqueContext(UniqueContext &&) noexcept = default;
-
-    /**
-     * 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();
-    }
-
-    /**
-     * Default move assignment operator.
-     *
-     * \return this
-     */
-    UniqueContext &operator=(UniqueContext &&) noexcept = delete;
-};
-
-/**
- * \class Error
- * \brief Base ECMAScript error class.
- * \warning Override the function create for your own exceptions
- */
-class Error {
-private:
-    int m_type{DUK_ERR_ERROR};
-    std::string m_message;
-
-protected:
-    /**
-     * Constructor with a type of error specified, specially designed for derived errors.
-     *
-     * \param type of error (e.g. DUK_ERR_ERROR)
-     * \param message the message
-     */
-    inline Error(int type, std::string message) noexcept
-        : m_type(type)
-        , m_message(std::move(message))
-    {
-    }
-
-public:
-    /**
-     * Constructor with a message.
-     *
-     * \param message the message
-     */
-    inline Error(std::string message) noexcept
-        : m_message(std::move(message))
-    {
-    }
-
-    /**
-     * Create the exception on the stack.
-     *
-     * \note the default implementation search for the global variables
-     * \param ctx the context
-     */
-    virtual void raise(duk_context *ctx) const
-    {
-        duk_error(ctx, m_type, "%s", m_message.c_str());
-    }
-};
-
-/**
- * \class EvalError
- * \brief Error in eval() function.
- */
-class EvalError : public Error {
-public:
-    /**
-     * Construct an EvalError.
-     *
-     * \param message the message
-     */
-    inline EvalError(std::string message) noexcept
-        : Error(DUK_ERR_EVAL_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \class RangeError
- * \brief Value is out of range.
- */
-class RangeError : public Error {
-public:
-    /**
-     * Construct an RangeError.
-     *
-     * \param message the message
-     */
-    inline RangeError(std::string message) noexcept
-        : Error(DUK_ERR_RANGE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \class ReferenceError
- * \brief Trying to use a variable that does not exist.
- */
-class ReferenceError : public Error {
-public:
-    /**
-     * Construct an ReferenceError.
-     *
-     * \param message the message
-     */
-    inline ReferenceError(std::string message) noexcept
-        : Error(DUK_ERR_REFERENCE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \class SyntaxError
- * \brief Syntax error in the script.
- */
-class SyntaxError : public Error {
-public:
-    /**
-     * Construct an SyntaxError.
-     *
-     * \param message the message
-     */
-    inline SyntaxError(std::string message) noexcept
-        : Error(DUK_ERR_SYNTAX_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \class TypeError
- * \brief Invalid type given.
- */
-class TypeError : public Error {
-public:
-    /**
-     * Construct an TypeError.
-     *
-     * \param message the message
-     */
-    inline TypeError(std::string message) noexcept
-        : Error(DUK_ERR_TYPE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \class URIError
- * \brief URI manipulation failure.
- */
-class URIError : public Error {
-public:
-    /**
-     * Construct an URIError.
-     *
-     * \param message the message
-     */
-    inline URIError(std::string message) noexcept
-        : Error(DUK_ERR_URI_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
- *
- * \param ctx the context
- * \param index the index
- * \param pop if true, also remove the exception from the stack
- * \return the information
- */
-inline Exception dukx_exception(duk_context *ctx, int index, bool pop = true)
-{
-    Exception ex;
-
-    index = duk_normalize_index(ctx, index);
-
-    duk_get_prop_string(ctx, index, "name");
-    ex.name = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "message");
-    ex.message = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "fileName");
-    ex.fileName = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "lineNumber");
-    ex.lineNumber = duk_to_int(ctx, -1);
-    duk_get_prop_string(ctx, index, "stack");
-    ex.stack = duk_to_string(ctx, -1);
-    duk_pop_n(ctx, 5);
-
-    if (pop)
-        duk_remove(ctx, index);
-
-    return ex;
-}
-
-/**
- * Enumerate an object or an array at the specified index.
- *
- * \param ctx the context
- * \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 dukx_enumerate(duk_context *ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func)
-{
-    duk_enum(ctx, index, flags);
-
-    while (duk_next(ctx, -1, getvalue)) {
-        func(ctx);
-        duk_pop_n(ctx, 1 + (getvalue ? 1 : 0));
-    }
-
-    duk_pop(ctx);
-}
-
-/**
- * Throw an ECMAScript exception.
- *
- * \param ctx the context
- * \param ex the exception
- */
-template <typename Exception>
-void dukx_throw(duk_context *ctx, const Exception &ex)
-{
-    ex.raise(ctx);
-}
-
-/**
- * Get a string, return 0 if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_get_std_string(duk_context *ctx, int index)
-{
-    duk_size_t size;
-    const char *text = duk_get_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Require a string, throws a JavaScript exception if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_require_std_string(duk_context *ctx, int index)
-{
-    duk_size_t size;
-    const char *text = duk_require_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Push a C++ string.
- *
- * \param ctx the context
- * \param str the string
- */
-inline void dukx_push_std_string(duk_context *ctx, const std::string &str)
-{
-    duk_push_lstring(ctx, str.data(), str.length());
-}
-
-/**
- * Get an array.
- *
- * \param ctx the context
- * \param index the array index
- * \param get the conversion function (e.g. duk_get_int)
- */
-template <typename Getter>
-auto dukx_get_array(duk_context *ctx, duk_idx_t index, Getter &&get)
-{
-    using T = decltype(get(ctx, 0));
-
-    std::vector<T> result;
-    std::size_t length = duk_get_length(ctx, index);
-
-    for (std::size_t i = 0; i < length; ++i) {
-        duk_get_prop_index(ctx, -1, i);
-        result.push_back(get(ctx, -1));
-        duk_pop(ctx);
-    }
-
-    return result;
-}
-
-/**
- * Push an array.
- *
- * \param ctx the context
- * \param values the values
- * \param push the function to push values
- */
-template <typename T, typename Pusher>
-void dukx_push_array(duk_context *ctx, const std::vector<T> &values, Pusher &&push)
-{
-    duk_push_array(ctx);
-
-    int i = 0;
-    for (auto x : values) {
-        push(ctx, x);
-        duk_put_prop_index(ctx, -2, i++);
-    }
-}
-
-} // !irccd
-
-#endif // !IRCCD_DUKTAPE_HPP
--- a/lib/irccd/dynlib.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,338 +0,0 @@
-/*
- * dynlib.hpp -- portable shared library 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_DYNLIB_HPP
-#define IRCCD_DYNLIB_HPP
-
-/**
- * \file dynlib.hpp
- * \brief Portable shared library loader.
- * \author David Demelier <markand@malikania.fr>
- */
-
-/**
- * \page Dynlib Dynlib
- * \brief Portable shared library loader.
- *
- * The dynlib module let you open shared libraries dynamically at runtime.
- *
- * ## Operating system support
- *
- * | System  | Support | Remarks            |
- * |---------|---------|--------------------|
- * | Apple   | Ok      |                    |
- * | FreeBSD | Ok      |                    |
- * | Linux   | Ok      | Needs -ldl library |
- * | Windows | Ok      |                    |
- *
- * ## How to export symbols
- *
- * When you want to dynamically load symbols from your shared library, make sure they are in a `extern "C"` block, if
- * not they will be [mangled][name-mangling].
- *
- * Note, this does not mean that you can't write C++ code, it just mean that you can't use namespaces and function
- * overloading.
- *
- * Example of **plugin.cpp**:
- *
- * ````cpp
- * #include <iostream>
- *
- * #include "dynlib.hpp"
- *
- * extern "C" {
- *
- * DYNLIB_EXPORT void plugin_load()
- * {
- *   std::cout << "Loading plugin" << std::endl;
- * }
- *
- * DYNLIB_EXPORT void plugin_unload()
- * {
- *   std::cout << "Unloading plugin" << std::endl;
- * }
- *
- * }
- * ````
- *
- * The \ref DYNLIB_EXPORT macro is necessary on some platforms to be sure that symbol will be visible. Make sure you always
- * add it before any function.
- *
- * To compile, see your compiler documentation or build system. For gcc you can use the following:
- *
- * ````
- * gcc -std=c++14 -shared plugin.cpp -o plugin.so
- * ````
- *
- * ## How to load the library
- *
- * The dynlib module will search for the library in various places, thus you can use relative paths names but be sure
- * that the library can be found. Otherwise, just use an absolute path to the file.
- *
- * ````cpp
- * #include <iostream>
- *
- * #include "dynlib.hpp"
- *
- * int main()
- * {
- *   try {
- *     Dynlib dso("./plugin" DYNLIB_SUFFIX);
- *   } catch (const std::exception &ex) {
- *     std::cerr << ex.what() << std::endl;
- *   }
- *
- *   return 0;
- * }
- * ````
- *
- * ## How to load symbol
- *
- * The last part is symbol loading, you muse use raw C function pointer and the Dynlib::sym function.
- *
- * ````cpp
- * #include <iostream>
- *
- * #include "dynlib.hpp"
- *
- * using PluginLoad = void (*)();
- * using PluginUnload = void (*)();
- *
- * int main()
- * {
- *    try {
- *        Dynlib dso("./plugin" DYNLIB_SUFFIX);
- *
- *        dso.sym<PluginLoad>("plugin_load")();
- *        dso.sym<PluginUnload>("plugin_unload")();
- *    } catch (const std::exception &ex) {
- *        std::cerr << ex.what() << std::endl;
- *    }
- *
- *    return 0;
- * }
- * ````
- *
- * [name-mangling]: https://en.wikipedia.org/wiki/Name_mangling
- */
-
-#include <stdexcept>
-#include <string>
-
-#if defined(_WIN32)
-#  include <windows.h>
-#else
-#  include <dlfcn.h>
-#endif
-
-/**
- * \brief Export the symbol.
- *
- * This is required on some platforms and you should put it before your function signature.
- *
- * \code{.cpp}
- * extern "C" {
- *
- * DYNLIB_EXPORT void my_function()
- * {
- * }
- *
- * }
- * \endcode
- */
-#if defined(_WIN32)
-#  define DYNLIB_EXPORT    __declspec(dllexport)
-#else
-#  define DYNLIB_EXPORT
-#endif
-
-/**
- * \brief Usual suffix for the library.
- *
- * This macro expands to the suffix convention for this platform.
- *
- * \code{.cpp}
- * Dynlib library("./myplugin" DYNLIB_SUFFIX);
- * \endcode
- *
- * \note Don't use the suffix expanded value shown in Doxygen as it may be wrong.
- */
-#if defined(_WIN32)
-#  define DYNLIB_SUFFIX ".dll"
-#elif defined(__APPLE__)
-#  define DYNLIB_SUFFIX ".dylib"
-#else
-#  define DYNLIB_SUFFIX ".so"
-#endif
-
-namespace irccd {
-
-/**
- * \class Dynlib
- * \brief Load a dynamic module.
- *
- * This class is a portable wrapper to load shared libraries on supported systems.
- */
-class Dynlib {
-private:
-#if defined(_WIN32)
-    using Handle    = HMODULE;
-    using Sym    = FARPROC;
-#else
-    using Handle    = void *;
-    using Sym    = void *;
-#endif
-
-public:
-    /**
-     * \brief Policy for symbol resolution.
-     */
-    enum Policy {
-        Immediately,        //!< load symbols immediately
-        Lazy            //!< load symbols when needed
-    };
-
-private:
-    Handle    m_handle;
-
-    Dynlib(const Dynlib &) = delete;
-    Dynlib &operator=(const Dynlib &) = delete;
-
-    Dynlib(Dynlib &&) = delete;
-    Dynlib &operator=(Dynlib &&) = delete;
-
-#if defined(_WIN32)
-    std::string error()
-    {
-        LPSTR error = nullptr;
-        std::string errmsg;
-
-        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;
-    }
-#endif
-
-public:
-    /**
-     * Constructor to load a shared module.
-     *
-     * \param path the absolute path
-     * \param policy the policy to load
-     * \throw std::runtime_error on error
-     */
-    inline Dynlib(const std::string &path, Policy policy = Immediately);
-
-    /**
-     * Close the library automatically.
-     */
-    inline ~Dynlib();
-
-    /**
-     * Get a symbol from the library.
-     *
-     * On some platforms the symbol must be manually exported.
-     *
-     * \param name the symbol
-     * \return the symbol
-     * \throw std::runtime_error on error
-     * \see DYNLIB_EXPORT
-     */
-    template <typename T>
-    inline T sym(const std::string &name);
-};
-
-#if defined(_WIN32)
-
-/*
- * Windows implementation
- * ------------------------------------------------------------------
- */
-
-Dynlib::Dynlib(const std::string &path, Policy)
-{
-    m_handle = LoadLibraryA(path.c_str());
-
-    if (m_handle == nullptr)
-        throw std::runtime_error(error());
-}
-
-Dynlib::~Dynlib()
-{
-    FreeLibrary(m_handle);
-    m_handle = nullptr;
-}
-
-template <typename T>
-T Dynlib::sym(const std::string &name)
-{
-    Sym sym = GetProcAddress(m_handle, name.c_str());
-
-    if (sym == nullptr)
-        throw std::runtime_error(error());
-
-    return reinterpret_cast<T>(sym);
-}
-
-#else
-
-/*
- * Unix implementation
- * ------------------------------------------------------------------
- */
-
-Dynlib::Dynlib(const std::string &path, Policy policy)
-{
-    m_handle = dlopen(path.c_str(), policy == Immediately ? RTLD_NOW : RTLD_LAZY);
-
-    if (m_handle == nullptr)
-        throw std::runtime_error(dlerror());
-}
-
-Dynlib::~Dynlib()
-{
-    dlclose(m_handle);
-    m_handle = nullptr;
-}
-
-template <typename T>
-T Dynlib::sym(const std::string &name)
-{
-    Sym sym = dlsym(m_handle, name.c_str());
-
-    if (sym == nullptr)
-        throw std::runtime_error(dlerror());
-
-    return reinterpret_cast<T>(sym);
-}
-
-#endif
-
-#endif // !IRCCD_DYNLIB_HPP
-
-} // !irccd
--- a/lib/irccd/elapsed-timer.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /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.hpp"
-
-using std::chrono::duration_cast;
-using std::chrono::high_resolution_clock;
-using std::chrono::milliseconds;
-
-namespace irccd {
-
-ElapsedTimer::ElapsedTimer() noexcept
-{
-    m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::pause() noexcept
-{
-    /*
-     * When we put the timer on pause, do not forget to set the already
-     * elapsed time.
-     */
-    (void)elapsed();
-    m_paused = true;
-}
-
-void ElapsedTimer::restart() noexcept
-{
-    m_paused = false;
-    m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::reset() noexcept
-{
-    m_elapsed = 0;
-    m_last = high_resolution_clock::now();
-}
-
-unsigned ElapsedTimer::elapsed() noexcept
-{
-    if (!m_paused) {
-        m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count();
-        m_last = high_resolution_clock::now();
-    }
-
-    return m_elapsed;
-}
-
-} // !irccd
--- a/lib/irccd/elapsed-timer.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * elapsed-timer.hpp -- 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_HPP
-#define IRCCD_ELAPSED_TIMER_HPP
-
-/**
- * \file elapsed-timer.hpp
- * \brief Measure elapsed time
- */
-
-#include <chrono>
-
-#include "sysconfig.hpp"
-
-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 {
-private:
-    using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
-
-    TimePoint m_last;
-    bool m_paused{false};
-    unsigned m_elapsed{0};
-
-public:
-    /**
-     * Construct the elapsed timer, start counting.
-     */
-    IRCCD_EXPORT ElapsedTimer() noexcept;
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~ElapsedTimer() = default;
-
-    /**
-     * Put the timer on pause, the already elapsed time is stored.
-     */
-    IRCCD_EXPORT void pause() noexcept;
-
-    /**
-     * Restart the timer, does not reset it.
-     */
-    IRCCD_EXPORT void restart() noexcept;
-
-    /**
-     * Reset the timer to 0.
-     */
-    IRCCD_EXPORT void reset() noexcept;
-
-    /**
-     * Get the number of elapsed milliseconds.
-     *
-     * \return the milliseconds
-     */
-    IRCCD_EXPORT unsigned elapsed() noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_ELAPSED_TIMER_HPP
--- a/lib/irccd/fs.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,503 +0,0 @@
-/*
- * fs.cpp -- filesystem 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.
- */
-
-#if defined(_WIN32)
-#   if !defined(_CRT_SECURE_NO_WARNINGS)
-#       define _CRT_SECURE_NO_WARNINGS
-#   endif
-#   if !defined(WIN32_LEAN_AND_MEAN)
-#       define WIN32_LEAN_AND_MEAN
-#   endif
-#endif
-
-#include <algorithm>
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <sstream>
-#include <stdexcept>
-
-#if defined(_WIN32)
-#   include <direct.h>
-#   include <windows.h>
-#   include <shlwapi.h>
-#else
-#   include <sys/types.h>
-#   include <dirent.h>
-#   include <unistd.h>
-#endif
-
-#include "fs.hpp"
-
-namespace irccd {
-
-namespace fs {
-
-namespace {
-
-/*
- * error.
- * ------------------------------------------------------------------
- *
- * Function to retrieve system error in Windows API.
- */
-#if defined(_WIN32)
-
-std::string error()
-{
-    LPSTR error = nullptr;
-    std::string errmsg = "Unknown error";
-
-    FormatMessageA(
-        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-        nullptr,
-        GetLastError(),
-        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-        (LPSTR)&error, 0, nullptr);
-
-    if (error) {
-        errmsg = std::string(error);
-        LocalFree(error);
-    }
-
-    return errmsg;
-}
-
-#endif
-
-/*
- * hasAccess.
- * ------------------------------------------------------------------
- *
- * Check if we have access to the file specified, mode is the same used as
- * std::fopen.
- */
-bool hasAccess(const std::string &path, const std::string &mode)
-{
-    assert(mode.length() == 1);
-    assert(mode[0] == 'r' || mode[0] == 'w');
-
-    auto fp = std::fopen(path.c_str(), mode.c_str());
-
-    if (fp == nullptr)
-        return false;
-
-    std::fclose(fp);
-
-    return true;
-}
-
-/*
- * typeOf.
- * ------------------------------------------------------------------
- *
- * Get the type of the specified file.
- *
- * Use GetFileAttributesA on Windows and stat if available.
- *
- * Receives the object as predicate parameter and return true on success.
- */
-#if defined(_WIN32)
-
-template <typename Predicate>
-bool typeOf(const std::string &path, Predicate &&predicate)
-{
-    DWORD result = GetFileAttributesA(path.c_str());
-
-    if (result == INVALID_FILE_ATTRIBUTES)
-        return false;
-
-    return predicate(result);
-}
-
-#elif defined(FS_HAVE_STAT)
-
-template <typename Predicate>
-bool typeOf(const std::string &path, Predicate &&predicate) noexcept
-{
-    struct stat st;
-
-    if (::stat(path.c_str(), &st) < 0)
-        return false;
-
-    return predicate(st);
-}
-
-#else
-
-template <typename Predicate>
-bool typeOf(const std::string &path, Predicate &&predicate) noexcept
-{
-    throw std::runtime_error(std::strerror(ENOSYS));
-}
-
-#endif
-
-} // !namespace
-
-/*
- * clean.
- * ------------------------------------------------------------------
- */
-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 += separator();
-
-    // Now converts all / to \\ for Windows and the opposite for Unix.
-#if defined(_WIN32)
-    std::replace(input.begin(), input.end(), '/', '\\');
-#else
-    std::replace(input.begin(), input.end(), '\\', '/');
-#endif
-
-    return input;
-}
-
-/*
- * baseName.
- * ------------------------------------------------------------------
- */
-std::string baseName(std::string path)
-{
-    auto pos = path.find_last_of("\\/");
-
-    if (pos != std::string::npos)
-        path = path.substr(pos + 1);
-
-    return path;
-}
-
-/*
- * dirName.
- * ------------------------------------------------------------------
- */
-std::string dirName(std::string path)
-{
-    auto pos = path.find_last_of("\\/");
-
-    if (pos == std::string::npos)
-        path = ".";
-    else
-        path = path.substr(0, pos);
-
-    return path;
-}
-
-/*
- * isAbsolute.
- * ------------------------------------------------------------------
- */
-bool isAbsolute(const std::string &path) noexcept
-{
-#if defined(_WIN32)
-    return !isRelative(path);
-#else
-    return path.size() > 0 && path[0] == '/';
-#endif
-}
-
-/*
- * isRelative.
- * ------------------------------------------------------------------
- */
-bool isRelative(const std::string &path) noexcept
-{
-#if defined(_WIN32)
-    return PathIsRelativeA(path.c_str()) == 1;
-#else
-    return !isAbsolute(path);
-#endif
-}
-
-/*
- * isReadable.
- * ------------------------------------------------------------------
- */
-bool isReadable(const std::string &path) noexcept
-{
-    return hasAccess(path, "r");
-}
-
-/*
- * isWritable.
- * ------------------------------------------------------------------
- */
-bool isWritable(const std::string &path) noexcept
-{
-    return hasAccess(path, "w");
-}
-
-/*
- * isFile.
- * ------------------------------------------------------------------
- */
-bool isFile(const std::string &path)
-{
-    return typeOf(path, [] (const auto &object) {
-#if defined(_WIN32)
-        return (object & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE;
-#elif defined(FS_HAVE_STAT)
-        return S_ISREG(object.st_mode);
-#endif
-    });
-}
-
-/*
- * isDirectory.
- * ------------------------------------------------------------------
- */
-bool isDirectory(const std::string &path)
-{
-    return typeOf(path, [] (const auto &object) {
-#if defined(_WIN32)
-        return (object & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
-#elif defined(FS_HAVE_STAT)
-        return S_ISDIR(object.st_mode);
-#endif
-    });
-}
-
-/*
- * isSymlink.
- * ------------------------------------------------------------------
- */
-bool isSymlink(const std::string &path)
-{
-    return typeOf(path, [] (const auto &object) {
-#if defined(_WIN32)
-        return (object & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
-#elif defined(FS_HAVE_STAT)
-        return S_ISLNK(object.st_mode);
-#endif
-    });
-}
-
-/*
- * stat.
- * ------------------------------------------------------------------
- */
-#if defined(FS_HAVE_STAT)
-
-struct stat stat(const std::string &path)
-{
-    struct stat st;
-
-    if (::stat(path.c_str(), &st) < 0)
-        throw std::runtime_error(std::strerror(errno));
-
-    return st;
-}
-
-#endif
-
-/*
- * exists.
- * ------------------------------------------------------------------
- */
-bool exists(const std::string &path) noexcept
-{
-#if defined(FS_HAVE_STAT)
-    struct stat st;
-
-    return ::stat(path.c_str(), &st) == 0;
-#else
-    return  hasAccess(path, "r");
-#endif
-}
-
-/*
- * readdir.
- * ------------------------------------------------------------------
- */
-std::vector<Entry> readdir(const std::string &path, int flags)
-{
-    std::vector<Entry> entries;
-
-#if defined(_WIN32)
-    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(error());
-
-    do {
-        Entry entry;
-
-        entry.name = fdata.cFileName;
-
-        if (entry.name == "." && !(flags & Dot))
-            continue;
-        if (entry.name == ".." && !(flags & DotDot))
-            continue;
-
-        switch (fdata.dwFileAttributes) {
-        case FILE_ATTRIBUTE_DIRECTORY:
-            entry.type = Entry::Dir;
-            break;
-        case FILE_ATTRIBUTE_NORMAL:
-            entry.type = Entry::File;
-            break;
-        case FILE_ATTRIBUTE_REPARSE_POINT:
-            entry.type = Entry::Link;
-            break;
-        default:
-            break;
-        }
-
-        entries.push_back(std::move(entry));
-    } while (FindNextFile(handle, &fdata) != 0);
-
-    FindClose(handle);
-#else
-    DIR *dp;
-    struct dirent *ent;
-
-    if ((dp = opendir(path.c_str())) == nullptr)
-        throw std::runtime_error(std::strerror(errno));
-
-    while ((ent = readdir(dp)) != nullptr) {
-        Entry entry;
-
-        entry.name = ent->d_name;
-        if (entry.name == "." && !(flags & Dot))
-            continue;
-        if (entry.name == ".." && !(flags & DotDot))
-            continue;
-
-        switch (ent->d_type) {
-        case DT_DIR:
-            entry.type = Entry::Dir;
-            break;
-        case DT_REG:
-            entry.type = Entry::File;
-            break;
-        case DT_LNK:
-            entry.type = Entry::Link;
-            break;
-        default:
-            break;
-        }
-
-        entries.push_back(std::move(entry));
-    }
-
-    closedir(dp);
-#endif
-
-    return entries;
-}
-
-/*
- * mkdir.
- * ------------------------------------------------------------------
- */
-void mkdir(const std::string &path, int mode)
-{
-    std::string::size_type next = 0;
-    std::string part;
-
-    for (;;) {
-        next = path.find_first_of("\\/", next);
-        part = path.substr(0, next);
-
-        if (!part.empty()) {
-#if defined(_WIN32)
-            (void)mode;
-
-            if (::_mkdir(part.c_str()) < 0 && errno != EEXIST)
-                throw std::runtime_error(std::strerror(errno));
-#else
-            if (::mkdir(part.c_str(), mode) < 0 && errno != EEXIST)
-                throw std::runtime_error(std::strerror(errno));
-#endif
-        }
-
-        if (next++ == std::string::npos)
-            break;
-    }
-}
-
-/*
- * rmdir.
- * ------------------------------------------------------------------
- */
-void rmdir(const std::string &base) noexcept
-{
-    try {
-        for (const auto &entry : readdir(base)) {
-            std::string path = base + separator() + entry.name;
-
-            if (entry.type == Entry::Dir)
-                rmdir(path);
-            else
-                ::remove(path.c_str());
-        }
-    } catch (...) {
-        // Silently discard to remove as much as possible.
-    }
-
-#if defined(_WIN32)
-    ::RemoveDirectoryA(base.c_str());
-#else
-    ::remove(base.c_str());
-#endif
-}
-
-/*
- * cwd.
- * ------------------------------------------------------------------
- */
-std::string cwd()
-{
-#if defined(_WIN32)
-    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
-}
-
-} // !irccd
-
-} // !fs
--- a/lib/irccd/fs.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-/*
- * fs.hpp -- filesystem 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 FS_HPP
-#define FS_HPP
-
-/**
- * \file fs.hpp
- * \brief Filesystem operations made easy.
- */
-
-/**
- * \cond FS_HIDDEN_SYMBOLS
- */
-
-#if !defined(FS_HAVE_STAT)
-#   if defined(_WIN32)
-#       define FS_HAVE_STAT
-#   elif defined(__linux__)
-#       define FS_HAVE_STAT
-#   elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
-#       define FS_HAVE_STAT
-#   elif defined(__APPLE__)
-#       define FS_HAVE_STAT
-#   endif
-#endif
-
-/**
- * \endcond
- */
-
-#if defined(FS_HAVE_STAT)
-#   include <sys/stat.h>
-#endif
-
-#include <regex>
-#include <string>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * \brief Filesystem namespace.
- */
-namespace fs {
-
-/**
- * \enum Flags
- * \brief Flags for readdir.
- */
-enum Flags {
-    Dot     = (1 << 0),         //!< if set, also lists "."
-    DotDot  = (1 << 1)          //!< if set, also lists ".."
-};
-
-/**
- * \class Entry
- * \brief Entry in the directory list.
- */
-class Entry {
-public:
-    /**
-     * \brief Describe the type of an entry
-     */
-    enum Type : char {
-        Unknown,                //!< File type is unknown,
-        File,                   //!< File is regular type,
-        Dir,                    //!< File is directory,
-        Link                    //!< File is link
-    };
-
-    std::string name;           //!< name of entry (base name)
-    Type type{Unknown};         //!< type of file
-};
-
-/**
- * Check if two entries are identical.
- *
- * \param e1 the first entry
- * \param e2 the second entry
- * \return true if they are identical
- */
-inline bool operator==(const Entry &e1, const Entry &e2) noexcept
-{
-    return e1.name == e2.name && e1.type == e2.type;
-}
-
-/**
- * Check if two entries are different.
- *
- * \param e1 the first entry
- * \param e2 the second entry
- * \return true if they are different
- */
-inline bool operator!=(const Entry &e1, const Entry &e2) noexcept
-{
-    return !(e1 == e2);
-}
-
-/**
- * Get the separator for that system.
- *
- * \return \ on Windows and / otherwise
- */
-inline char separator() noexcept
-{
-#if defined(_WIN32)
-    return '\\';
-#else
-    return '/';
-#endif
-}
-
-/**
- * Clean a path by removing any extra / or \ and add a trailing one.
- *
- * \param path the path
- * \return the updated path
- */
-IRCCD_EXPORT std::string clean(std::string path);
-
-/**
- * Get the base name from a path.
- *
- * Example, baseName("/etc/foo.conf") // foo.conf
- *
- * \param path the path
- * \return the base name
- */
-IRCCD_EXPORT std::string baseName(std::string path);
-
-/**
- * Get the parent directory from a path.
- *
- * Example, dirName("/etc/foo.conf") // /etc
- *
- * \param path the path
- * \return the parent directory
- */
-IRCCD_EXPORT std::string dirName(std::string path);
-
-#if defined(FS_HAVE_STAT)
-
-/**
- * Get stat information.
- *
- * \param path the path
- * \return the stat information
- * \throw std::runtime_error on failure
- */
-IRCCD_EXPORT struct stat stat(const std::string &path);
-
-#endif // !HAVE_STAT
-
-/**
- * Check if a file exists.
- *
- * If HAVE_ACCESS is defined, the function access is used, otherwise stat is
- * used.
- *
- * \param path the path to check
- * \return true if the path exists
- */
-IRCCD_EXPORT bool exists(const std::string &path) noexcept;
-
-/**
- * Check if the path is absolute.
- *
- * \param path the path
- * \return true if the path is absolute
- */
-IRCCD_EXPORT bool isAbsolute(const std::string &path) noexcept;
-
-/**
- * Check if the path is relative.
- *
- * \param path the path
- * \return true if the path is absolute
- */
-IRCCD_EXPORT bool isRelative(const std::string &path) noexcept;
-
-/**
- * Check if the file is readable.
- *
- * \param path the path
- * \return true if has read access
- */
-IRCCD_EXPORT bool isReadable(const std::string &path) noexcept;
-
-/**
- * Check if the file is writable.
- *
- * \param path the path
- * \return true if has write access
- */
-IRCCD_EXPORT bool isWritable(const std::string &path) noexcept;
-
-/**
- * Check if the file is a regular file.
- *
- * \param path the path
- * \return true if it is a file and false if not or not readable
- * \throw std::runtime_error if the operation is not supported
- */
-IRCCD_EXPORT bool isFile(const std::string &path);
-
-/**
- * Check if the file is a directory.
- *
- * \param path the path
- * \return true if it is a directory and false if not or not readable
- * \throw std::runtime_error if the operation is not supported
- */
-IRCCD_EXPORT bool isDirectory(const std::string &path);
-
-/**
- * Check if the file is a symbolic link.
- *
- * \param path the path
- * \return true if it is a symbolic link and false if not or not readable
- * \throw std::runtime_error if the operation is not supported
- */
-IRCCD_EXPORT bool isSymlink(const std::string &path);
-
-/**
- * Read a directory and return a list of entries (not recursive).
- *
- * \param path the directory path
- * \param flags the optional flags (see Flags)
- * \return the list of entries
- * \throw std::runtime_error on failure
- */
-IRCCD_EXPORT std::vector<Entry> readdir(const std::string &path, int flags = 0);
-
-/**
- * Create a directory recursively.
- *
- * \param path the path
- * \param mode the optional mode (not always supported)
- * \throw std::runtime_error on failure
- * \post all intermediate directories are created
- */
-IRCCD_EXPORT void mkdir(const std::string &path, int mode = 0700);
-
-/**
- * Remove a directory recursively.
- *
- * If errors happens, they are silently discarded to remove as much as possible.
- *
- * \param path the path
- */
-IRCCD_EXPORT void rmdir(const std::string &path) noexcept;
-
-/**
- * Search an item recursively.
- *
- * The predicate must have the following signature:
- *  void f(const std::string &base, const Entry &entry)
- *
- * Where:
- *   - base is the current parent directory in the tree
- *   - entry is the current entry
- *
- * \param base the base directory
- * \param predicate the predicate
- * \return the full path name to the file or empty string if never found
- * \throw std::runtime_error on read errors
- */
-template <typename Predicate>
-std::string findIf(const std::string &base, Predicate &&predicate)
-{
-    /*
-     * Do not go deeply to the tree before testing all files in the current
-     * directory for performances reasons, we iterate this directory to search
-     * for the entry name and iterate again over all sub directories if not
-     * found.
-     */
-    std::string path;
-    std::vector<Entry> entries = readdir(base);
-
-    for (const auto &entry : entries) {
-        if (predicate(base, entry)) {
-            path = base + separator() + entry.name;
-            break;
-        }
-    }
-
-    if (!path.empty())
-        return path;
-
-    for (const auto &entry : entries) {
-        if (entry.type != Entry::Dir)
-            continue;
-
-        path = findIf(base + separator() + entry.name, std::forward<Predicate>(predicate));
-
-        if (!path.empty())
-            break;
-    }
-
-    return path;
-}
-
-/**
- * Find a file by name recursively.
- *
- * \param base the base directory
- * \param name the file name
- * \return the full path name to the file or empty string if never found
- * \throw std::runtime_error on read errors
- */
-inline std::string find(const std::string &base, const std::string &name)
-{
-    return findIf(base, [&] (const auto &, const auto &entry) { return entry.name == name; });
-}
-
-/**
- * Overload by regular expression.
- *
- * \param base the base directory
- * \param regex the regular expression
- * \return the full path name to the file or empty string if never found
- * \throw std::runtime_error on read errors
- */
-inline std::string find(const std::string &base, const std::regex &regex)
-{
-    return findIf(base, [&] (const auto &, const auto &entry) { return std::regex_match(entry.name, regex); });
-}
-
-/**
- * Get the current working directory.
- *
- * \return the current working directory
- * \throw std::runtime_error on failure
- */
-IRCCD_EXPORT std::string cwd();
-
-} // !fs
-
-} // !irccd
-
-#endif // !FS_HPP
--- a/lib/irccd/ini.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,420 +0,0 @@
-/*
- * ini.cpp -- extended .ini file 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 <cctype>
-#include <cstring>
-#include <iostream>
-#include <iterator>
-#include <fstream>
-#include <sstream>
-#include <stdexcept>
-
-// for PathIsRelative.
-#if defined(_WIN32)
-#   if !defined(WIN32_LEAN_AND_MEAN)
-#       define WIN32_LEAN_AND_MEAN
-#   endif
-
-#   include <shlwapi.h>
-#endif
-
-#include "ini.hpp"
-
-namespace irccd {
-
-namespace {
-
-using namespace 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 analyseLine(int &line, int &column, StreamIterator &it) noexcept
-{
-    assert(*it == '\n');
-
-    ++ line;
-    ++ it;
-    column = 0;
-}
-
-void analyseComment(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-    assert(*it == '#');
-
-    while (it != end && *it != '\n') {
-        ++ column;
-        ++ it;
-    }
-}
-
-void analyseSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-    assert(isSpace(*it));
-
-    while (it != end && isSpace(*it)) {
-        ++ column;
-        ++ it;
-    }
-}
-
-void analyseList(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 analyseSection(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>");
-    if (value.empty())
-        throw Error(line, column, "empty section name");
-
-    // Remove ']'.
-    ++ it;
-
-    list.emplace_back(Token::Section, line, save, std::move(value));
-}
-
-void analyseAssign(Tokens &list, int &line, int &column, StreamIterator &it)
-{
-    assert(*it == '=');
-
-    list.push_back({ Token::Assign, line, column++ });
-    ++ it;
-}
-
-void analyseQuotedWord(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 analyseWord(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 analyseInclude(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 });
-}
-
-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, const std::string &path, 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());
-
-    std::string value = (it++)->value();
-    std::string file;
-
-    if (!isAbsolute(value))
-#if defined(_WIN32)
-        file = path + "\\" + value;
-#else
-        file = path + "/" + value;
-#endif
-    else
-        file = value;
-
-    for (const auto &sc : readFile(file))
-        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));
-}
-
-} // !namespace
-
-namespace ini {
-
-Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end)
-{
-    Tokens list;
-    int line = 1;
-    int column = 0;
-
-    while (it != end) {
-        if (*it == '\n')
-            analyseLine(line, column, it);
-        else if (*it == '#')
-            analyseComment(column, it, end);
-        else if (*it == '[')
-            analyseSection(list, line, column, it, end);
-        else if (*it == '=')
-            analyseAssign(list, line, column, it);
-        else if (isSpace(*it))
-            analyseSpaces(column, it, end);
-        else if (*it == '@')
-            analyseInclude(list, line, column, it, end);
-        else if (isQuote(*it))
-            analyseQuotedWord(list, line, column, it, end);
-        else if (isList(*it))
-            analyseList(list, line, column, it);
-        else
-            analyseWord(list, line, column, it, end);
-    }
-
-    return list;
-}
-
-Tokens analyse(std::istream &stream)
-{
-    return analyse(std::istreambuf_iterator<char>(stream), {});
-}
-
-Document parse(const Tokens &tokens, const std::string &path)
-{
-    Document doc;
-    TokenIterator it = tokens.cbegin();
-    TokenIterator end = tokens.cend();
-
-    while (it != end) {
-        switch (it->type()) {
-        case Token::Include:
-            parseInclude(doc, path, it, end);
-            break;
-        case Token::Section:
-            parseSection(doc, it, end);
-            break;
-        default:
-            throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document");
-        }
-    }
-
-    return doc;
-}
-
-Document readFile(const std::string &filename)
-{
-    // Get parent path.
-    auto parent = filename;
-    auto pos = parent.find_last_of("/\\");
-
-    if (pos != std::string::npos)
-        parent.erase(pos);
-    else
-        parent = ".";
-
-    std::ifstream input(filename);
-
-    if (!input)
-        throw Error(0, 0, std::strerror(errno));
-
-    return parse(analyse(input), parent);
-}
-
-Document readString(const std::string &buffer)
-{
-    std::istringstream iss(buffer);
-
-    return parse(analyse(iss));
-}
-
-void dump(const Tokens &tokens)
-{
-    for (const Token &token: tokens)
-        // TODO: add better description
-        std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl;
-}
-
-} // !ini
-
-} // !irccd
--- a/lib/irccd/ini.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,616 +0,0 @@
-/*
- * ini.hpp -- extended .ini file 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 INI_HPP
-#define INI_HPP
-
-/**
- * \file ini.hpp
- * \brief Extended .ini file parser.
- * \author David Demelier <markand@malikania.fr>
- */
-
-/**
- * \page Ini Ini
- * \brief Extended .ini file parser.
- *
- *   - \subpage ini-syntax
- */
-
-/**
- * \page ini-syntax Syntax
- * \brief File syntax.
- *
- * The syntax is similar to most of `.ini` implementations as:
- *
- *   - a section is delimited by `[name]` can be redefined multiple times,
- *   - an option **must** always be defined in a section,
- *   - empty options must be surrounded by quotes,
- *   - lists can not includes trailing commas,
- *   - include statement must always be at the beginning of files (in no sections),
- *   - comments starts with # until the end of line,
- *   - options with spaces **must** use quotes.
- *
- * # Basic file
- *
- * ````ini
- * # This is a comment.
- * [section]
- * option1 = value1
- * option2 = "value 2 with spaces"    # comment is also allowed here
- * ````
- *
- * # Redefinition
- *
- * Sections can be redefined multiple times and are kept the order they are seen.
- *
- * ````ini
- * [section]
- * value = "1"
- * 
- * [section]
- * value = "2"
- * ````
- *
- * The ini::Document object will contains two ini::Section.
- *
- * # Lists
- *
- * Lists are defined using `()` and commas, like values, they may have quotes.
- *
- * ````ini
- * [section]
- * names = ( "x1", "x2" )
- *
- * # This is also allowed
- * biglist = (
- *   "abc",
- *   "def"
- * )
- * ````
- *
- * # Include statement
- *
- * You can split a file into several pieces, if the include statement contains a relative path, the path will be relative
- * to the current file being parsed.
- *
- * You **must** use the include statement before any section.
- *
- * If the file contains spaces, use quotes.
- *
- * ````ini
- * # main.conf
- * @include "foo.conf"
- *
- * # foo.conf
- * [section]
- * option1 = value1
- * ````
- */
-
-#include <algorithm>
-#include <cassert>
-#include <exception>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-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 line the line
-     * \param column the column
-     * \param msg the message
-     */
-    inline Error(int line, int column, std::string msg) noexcept
-        : m_line(line)
-        , m_column(column)
-        , m_message(std::move(msg))
-    {
-    }
-
-    /**
-     * 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 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.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     */
-    inline Option(std::string key) noexcept
-        : std::vector<std::string>()
-        , m_key(std::move(key))
-    {
-        assert(!m_key.empty());
-    }
-
-    /**
-     * Construct a single option.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     * \param value the value
-     */
-    inline Option(std::string key, std::string value) noexcept
-        : m_key(std::move(key))
-    {
-        assert(!m_key.empty());
-
-        push_back(std::move(value));
-    }
-
-    /**
-     * Construct a list option.
-     *
-     * \pre key must not be empty
-     * \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))
-    {
-        assert(!m_key.empty());
-    }
-
-    /**
-     * 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.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     */
-    inline Section(std::string key) noexcept
-        : m_key(std::move(key))
-    {
-        assert(!m_key.empty());
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * 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;
-        });
-    }
-
-    /**
-     * Access an option at the specified key.
-     *
-     * \param key the key
-     * \return the option
-     * \pre contains(key) must return true
-     */
-    inline Option &operator[](const std::string &key)
-    {
-        assert(contains(key));
-
-        return *find(key);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \param key the key
-     * \return the option
-     * \pre contains(key) must return true
-     */
-    inline const Option &operator[](const std::string &key) const
-    {
-        assert(contains(key));
-
-        return *find(key);
-    }
-
-    /**
-     * Inherited operators.
-     */
-    using std::vector<Option>::operator[];
-};
-
-/**
- * \class Document
- * \brief Ini document description.
- * \see readFile
- * \see readString
- */
-class Document : public std::vector<Section> {
-public:
-    /**
-     * Check if a document has a specific section.
-     *
-     * \param key the key
-     * \return true if the document contains the section
-     */
-    inline bool contains(const std::string &key) const noexcept
-    {
-        return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end();
-    }
-
-    /**
-     * 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;
-        });
-    }
-
-    /**
-     * Access a section at the specified key.
-     *
-     * \param key the key
-     * \return the section
-     * \pre contains(key) must return true
-     */
-    inline Section &operator[](const std::string &key)
-    {
-        assert(contains(key));
-
-        return *find(key);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \param key the key
-     * \return the section
-     * \pre contains(key) must return true
-     */
-    inline const Section &operator[](const std::string &key) const
-    {
-        assert(contains(key));
-
-        return *find(key);
-    }
-
-    /**
-     * Inherited operators.
-     */
-    using std::vector<Section>::operator[];
-};
-
-/**
- * Analyse a stream and detect potential syntax errors. This does not parse the file like including other
- * files in include statement.
- *
- * It does only analysis, for example if an option is defined under no section, this does not trigger an
- * error while it's invalid.
- *
- * \param it the iterator
- * \param end where to stop
- * \return the list of tokens
- * \throws Error on errors
- */
-IRCCD_EXPORT Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
-
-/**
- * Overloaded function for stream.
- *
- * \param stream the stream
- * \return the list of tokens
- * \throws Error on errors
- */
-IRCCD_EXPORT Tokens analyse(std::istream &stream);
-
-/**
- * Parse the produced tokens.
- *
- * \param tokens the tokens
- * \param path the parent path
- * \return the document
- * \throw Error on errors
- */
-IRCCD_EXPORT Document parse(const Tokens &tokens, const std::string &path = ".");
-
-/**
- * Parse a file.
- *
- * \param filename the file name
- * \return the document
- * \throw Error on errors
- */
-IRCCD_EXPORT Document readFile(const std::string &filename);
-
-/**
- * Parse a string.
- *
- * If the string contains include statements, they are relative to the current working directory.
- *
- * \param buffer the buffer
- * \return the document
- * \throw Error on errors
- */
-IRCCD_EXPORT Document readString(const std::string &buffer);
-
-/**
- * Show all tokens and their description.
- *
- * \param tokens the tokens
- */
-IRCCD_EXPORT void dump(const Tokens &tokens);
-
-} // !ini
-
-} // !irccd
-
-#endif // !INI_HPP
--- a/lib/irccd/irccd.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +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 "irccd.hpp"
-#include "logger.hpp"
-#include "net.hpp"
-#include "service-command.hpp"
-#include "service-interrupt.hpp"
-#include "service-module.hpp"
-#include "service-plugin.hpp"
-#include "service-rule.hpp"
-#include "service-server.hpp"
-#include "service-transport.hpp"
-#include "util.hpp"
-
-using namespace std;
-using namespace std::placeholders;
-using namespace std::string_literals;
-
-namespace irccd {
-
-Irccd::Irccd()
-    : m_commandService(std::make_shared<CommandService>())
-    , m_interruptService(std::make_shared<InterruptService>())
-    , m_servers(std::make_shared<ServerService>(*this))
-    , m_transports(std::make_shared<TransportService>(*this))
-    , m_ruleService(std::make_shared<RuleService>())
-    , m_moduleService(std::make_shared<ModuleService>())
-    , m_plugins(std::make_shared<PluginService>(*this))
-{
-}
-
-void Irccd::post(std::function<void (Irccd &)> ev) noexcept
-{
-    std::lock_guard<mutex> lock(m_mutex);
-
-    m_events.push_back(move(ev));
-    m_interruptService->interrupt();
-}
-
-void Irccd::run()
-{
-    while (m_running) {
-        util::poller::poll(250, *m_interruptService, *m_servers, *m_transports);
-        dispatch();
-    }
-}
-
-void Irccd::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    util::poller::prepare(in, out, max, *m_interruptService, *m_servers, *m_transports);
-}
-
-void Irccd::sync(fd_set &in, fd_set &out)
-{
-    util::poller::sync(in, out, *m_interruptService, *m_servers, *m_transports);
-}
-
-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<std::function<void (Irccd &)>> copy;
-
-    {
-        std::lock_guard<mutex> lock(m_mutex);
-
-        copy = move(m_events);
-        m_events.clear();
-    }
-
-    if (copy.size() > 0)
-        log::debug() << "irccd: dispatching " << copy.size() << " event" << (copy.size() > 1 ? "s" : "") << endl;
-
-    for (auto &ev : copy)
-        ev(*this);
-}
-
-void Irccd::stop()
-{
-    log::debug() << "irccd: requesting to stop now" << endl;
-
-    m_running = false;
-    m_interruptService->interrupt();
-}
-
-} // !irccd
--- a/lib/irccd/irccd.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/*
- * irccd.hpp -- 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_HPP
-#define IRCCD_HPP
-
-/**
- * \file irccd.hpp
- * \brief Base class for irccd front end.
- */
-
-#include <atomic>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include "net.hpp"
-#include "sysconfig.hpp"
-
-/**
- * \brief Main irccd namespace
- */
-namespace irccd {
-
-class CommandService;
-class InterruptService;
-class ModuleService;
-class PluginService;
-class RuleService;
-class ServerService;
-class TransportService;
-
-/**
- * \class Irccd
- * \brief Irccd main instance.
- */
-class Irccd {
-private:
-    // Main loop stuff.
-    std::atomic<bool> m_running{true};
-    std::mutex m_mutex;
-    std::vector<std::function<void (Irccd &)>> m_events;
-
-    // Services.
-    std::shared_ptr<CommandService> m_commandService;
-    std::shared_ptr<InterruptService> m_interruptService;
-    std::shared_ptr<ServerService> m_servers;
-    std::shared_ptr<TransportService> m_transports;
-    std::shared_ptr<RuleService> m_ruleService;
-    std::shared_ptr<ModuleService> m_moduleService;
-    std::shared_ptr<PluginService> m_plugins;
-
-    // Not copyable and not movable because services has references to irccd.
-    Irccd(const Irccd &) = delete;
-    Irccd(Irccd &&) = delete;
-
-    Irccd &operator=(const Irccd &) = delete;
-    Irccd &operator=(Irccd &&) = delete;
-
-public:
-    /**
-     * Prepare standard services.
-     */
-    IRCCD_EXPORT Irccd();
-
-    /**
-     * Access the command service.
-     *
-     * \return the service
-     */
-    inline CommandService &commands() noexcept
-    {
-        return *m_commandService;
-    }
-
-    /**
-     * Access the server service.
-     *
-     * \return the service
-     */
-    inline ServerService &servers() noexcept
-    {
-        return *m_servers;
-    }
-
-    /**
-     * Access the transport service.
-     *
-     * \return the service
-     */
-    inline TransportService &transports() noexcept
-    {
-        return *m_transports;
-    }
-
-    /**
-     * Access the rule service.
-     *
-     * \return the service
-     */
-    inline RuleService &rules() noexcept
-    {
-        return *m_ruleService;
-    }
-
-    /**
-     * Access the module service.
-     *
-     * \return the service
-     */
-    inline ModuleService &modules() noexcept
-    {
-        return *m_moduleService;
-    }
-
-    /**
-     * Access the plugin service.
-     *
-     * \return the service
-     */
-    inline PluginService &plugins() noexcept
-    {
-        return *m_plugins;
-    }
-
-    /**
-     * Prepare the services for selection.
-     *
-     * \param in the input set
-     * \param out the output set
-     * \param max the maximum handle
-     */
-    IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * Synchronize the services.
-     *
-     * \param in the input set
-     * \param out the output set
-     */
-    IRCCD_EXPORT void sync(fd_set &in, fd_set &out);
-
-    /**
-     * 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
-     */
-    IRCCD_EXPORT void post(std::function<void (Irccd &)> ev) noexcept;
-
-    /**
-     * Loop forever by calling poll() and dispatch() indefinitely.
-     */
-    IRCCD_EXPORT void run();
-
-    /**
-     * Dispatch the pending events, usually after calling poll().
-     */
-    IRCCD_EXPORT void dispatch();
-
-    /**
-     * Request to stop, usually from a signal.
-     */
-    IRCCD_EXPORT void stop();
-};
-
-} // !irccd
-
-#endif // !IRCCD_HPP
--- a/lib/irccd/irccdctl.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,646 +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 <format.h>
-
-#include "command.hpp"
-#include "client.hpp"
-#include "elapsed-timer.hpp"
-#include "fs.hpp"
-#include "ini.hpp"
-#include "irccdctl.hpp"
-#include "logger.hpp"
-#include "options.hpp"
-#include "path.hpp"
-#include "system.hpp"
-#include "util.hpp"
-
-using namespace std::string_literals;
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-void Irccdctl::usage() const
-{
-    bool first = true;
-
-    for (const auto &cmd : m_commandService.commands()) {
-        log::warning() << (first ? "usage: " : "       ") << sys::programName() << " "
-                       << cmd->usage() << std::endl;
-        first = false;
-    }
-
-    std::exit(1);
-}
-
-void Irccdctl::help() const
-{
-    log::warning() << "usage: " << sys::programName() << " [options...] <command> [command-options...] [command-args...]\n\n";
-    log::warning() << "General options:\n";
-    log::warning() << "\t-c, --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() << "Available commands:\n";
-
-    for (const auto &cmd : m_commandService.commands())
-        log::warning() << "\t" << std::left << std::setw(32)
-                       << cmd->name() << cmd->description() << std::endl;
-
-    log::warning() << "\nFor more information on a command, type " << sys::programName() << " help <command>" << std::endl;
-
-    std::exit(1);
-}
-
-/*
- * Configuration file parsing.
- * -------------------------------------------------------------------
- */
-
-/*
- * readConnectIp
- * -------------------------------------------------------------------
- *
- * Extract IP connection information from the config file.
- *
- * [connect]
- * type = "ip"
- * host = "ip or hostname"
- * port = "port number or service"
- * domain = "ipv4 or ipv6" (Optional, default: ipv4)
- * ssl = true | false
- */
-void Irccdctl::readConnectIp(const ini::Section &sc)
-{
-    ini::Section::const_iterator it;
-
-    std::string host, port;
-
-    if ((it = sc.find("host")) == sc.end())
-        throw std::invalid_argument("missing host parameter");
-
-    host = it->value();
-
-    if ((it = sc.find("port")) == sc.end())
-        throw std::invalid_argument("missing port parameter");
-
-    port = it->value();
-
-    int domain = AF_INET;
-
-    if ((it = sc.find("domain")) != sc.end()) {
-        if (it->value() == "ipv6")
-            domain = AF_INET6;
-        else if (it->value() == "ipv4")
-            domain = AF_INET;
-        else
-            throw std::invalid_argument("invalid domain: " + it->value());
-    }
-
-    m_address = net::resolveOne(host, port, domain, SOCK_STREAM);
-
-    if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value()))
-        m_connection = std::make_unique<TlsClient>();
-    else
-        m_connection = std::make_unique<Client>();
-}
-
-/*
- * readConnectLocal
- * -------------------------------------------------------------------
- *
- * Extract local connection for Unix.
- *
- * [connect]
- * type = "unix"
- * path = "path to socket file"
- */
-void Irccdctl::readConnectLocal(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_address = net::local::create(it->value());
-    m_connection = std::make_unique<Client>();
-#else
-    (void)sc;
-
-    throw std::invalid_argument("unix connection not supported on Windows");
-#endif
-}
-
-/*
- * readConnect
- * -------------------------------------------------------------------
- *
- * Generic function for reading the [connect] section.
- */
-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")
-        readConnectLocal(sc);
-    else
-        throw std::invalid_argument("invalid type given: " + it->value());
-
-    auto password = sc.find("password");
-
-    if (password != sc.end())
-        m_connection->setPassword(password->value());
-}
-
-/*
- * readGeneral
- * -------------------------------------------------------------------
- *
- * Read the general section.
- *
- * [general]
- * verbose = true
- */
-void Irccdctl::readGeneral(const ini::Section &sc)
-{
-    auto verbose = sc.find("verbose");
-
-    if (verbose != sc.end())
-        log::setVerbose(util::isBoolean(verbose->value()));
-}
-
-/*
- * readAliases
- * -------------------------------------------------------------------
- *
- * Read aliases for irccdctl.
- *
- * [alias]
- * name = ( "command", "arg1, "...", "argn" )
- */
-void Irccdctl::readAliases(const ini::Section &sc)
-{
-    for (const auto &option : sc) {
-        // This is the alias name.
-        Alias alias(option.key());
-
-        // Iterate over the list of commands to execute for this alias.
-        for (const auto &repl : option) {
-            // This is the alias split string.
-            auto 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.
-            auto command = list[0];
-
-            // Remove command name and puts arguments.
-            alias.push_back({std::move(command), std::vector<AliasArg>(list.begin() + 1, list.end())});
-        }
-
-        m_aliases.emplace(option.key(), std::move(alias));
-    }
-}
-
-void Irccdctl::read(const std::string &path)
-{
-    try {
-        ini::Document doc = ini::readFile(path);
-        ini::Document::const_iterator it;
-
-        if (!m_connection && (it = doc.find("connect")) != doc.end())
-            readConnect(*it);
-        if ((it = doc.find("general")) != doc.end())
-            readGeneral(*it);
-        if ((it = doc.find("alias")) != doc.end())
-            readAliases(*it);
-    } catch (const std::exception &ex) {
-        log::warning() << path << ": " << ex.what() << std::endl;
-    }
-}
-
-/*
- * Command line parsing.
- * -------------------------------------------------------------------
- */
-
-/*
- * parseConnectIp
- * ------------------------------------------------------------------
- *
- * Parse internet connection from command line.
- *
- * -t ip | ipv6
- * -h host or ip
- * -p port
- */
-void Irccdctl::parseConnectIp(const option::Result &options)
-{
-    option::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).
-    std::string port;
-
-    if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end())
-        throw std::invalid_argument("missing port argument (-p or --port)");
-
-    port = it->second;
-
-    // Domain
-    int domain = AF_INET;
-
-    if ((it = options.find("-t")) != options.end())
-        domain = it->second == "ipv6" ? AF_INET6 : AF_INET;
-    else if ((it = options.find("--type")) != options.end())
-        domain = it->second == "ipv6" ? AF_INET6: AF_INET;
-
-    m_address = net::resolveOne(host, port, domain, SOCK_STREAM);
-    m_connection = std::make_unique<Client>();
-}
-
-/*
- * parseConnectLocal
- * ------------------------------------------------------------------
- *
- * Parse local connection.
- *
- * -P file
- */
-void Irccdctl::parseConnectLocal(const option::Result &options)
-{
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-    option::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_address = net::local::create(it->second, false);
-    m_connection = std::make_unique<Client>();
-#else
-    (void)options;
-
-    throw std::invalid_argument("unix connection not supported on Windows");
-#endif
-}
-
-/*
- * parseConnect
- * ------------------------------------------------------------------
- *
- * Generic parsing of command line option for connection.
- */
-void Irccdctl::parseConnect(const option::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);
-    if (it->second == "unix")
-        return parseConnectLocal(options);
-
-    throw std::invalid_argument("invalid type given: " + it->second);
-}
-
-option::Result Irccdctl::parse(int &argc, char **&argv)
-{
-    // 1. Parse command line options.
-    option::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   }
-    };
-
-    option::Result result;
-
-    try {
-        result = option::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("{}: {}"_format(sys::programName(), ex.what()));
-        usage();
-    }
-
-    return result;
-}
-
-nlohmann::json Irccdctl::waitMessage(const std::string id)
-{
-    ElapsedTimer timer;
-
-    while (m_messages.empty() && m_connection->isConnected() && timer.elapsed() < m_timeout)
-        util::poller::poll(250, *m_connection);
-
-    if (m_messages.empty())
-        return nlohmann::json();
-
-    nlohmann::json value;
-
-    if (id == "") {
-        value = m_messages[0];
-        m_messages.erase(m_messages.begin());
-    } else {
-        auto it = std::find_if(m_messages.begin(), m_messages.end(), [&] (const auto &v) {
-            auto rt = v.find("response");
-
-            if (v.count("error") > 0 || (rt != v.end() && rt->is_string() && *rt == id))
-                return true;
-
-            return false;
-        });
-
-        // Remove the previous messages.
-        if (it != m_messages.end()) {
-            value = *it;
-            m_messages.erase(m_messages.begin(), it + 1);
-        }
-    }
-
-    auto error = value.find("error");
-
-    if (error != value.end() && error->is_string())
-        throw std::runtime_error(error->template get<std::string>());
-
-    return value;
-}
-
-nlohmann::json Irccdctl::waitEvent()
-{
-    ElapsedTimer timer;
-
-    while (m_events.empty() && m_connection->isConnected() && timer.elapsed() < m_timeout)
-        util::poller::poll(250, *m_connection);
-
-    if (m_events.empty())
-        return nullptr;
-
-    auto first = m_events.front();
-    m_events.erase(m_events.begin());
-
-    return first;
-}
-
-nlohmann::json Irccdctl::exec(const Command &cmd, std::vector<std::string> args)
-{
-    // 1. Build options from command line arguments.
-    option::Options def;
-
-    for (const auto &opt : cmd.options()) {
-        // parser::read needs '-' and '--' so add them.
-        if (!opt.simpleKey().empty())
-            def.emplace("-"s + opt.simpleKey(), !opt.arg().empty());
-        if (!opt.longKey().empty())
-            def.emplace("--"s + opt.longKey(), !opt.arg().empty());
-    }
-
-    // 2. Parse them, remove them from args (in parser::read) and build the map with id.
-    CommandRequest::Options requestOptions;
-
-    for (const auto &pair : option::read(args, def)) {
-        auto options = cmd.options();
-        auto it = std::find_if(options.begin(), options.end(), [&] (const auto &opt) {
-            return ("-"s + opt.simpleKey()) == pair.first || ("--"s + opt.longKey()) == pair.first;
-        });
-
-        requestOptions.emplace(it->id(), pair.second);
-    }
-
-    // 3. Check number of arguments.
-    if (args.size() < cmd.min())
-        throw std::runtime_error("too few arguments");
-
-    /*
-     * 4. Construct the request, if the returned value is not an object, do not
-     * send anything (e.g. help).
-     */
-    auto request = cmd.request(*this, CommandRequest(std::move(requestOptions), std::move(args)));
-
-    if (!request.is_object())
-        throw std::invalid_argument("command has returned invalid request");
-
-    request.push_back({"command", cmd.name()});
-
-    // 5. Send the command.
-    m_connection->request(request);
-
-    // 6. Returns the response.
-    return waitMessage(cmd.name());
-}
-
-std::vector<nlohmann::json> Irccdctl::exec(const Alias &alias, std::vector<std::string> argsCopy)
-{
-    std::vector<nlohmann::json> values;
-
-    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 auto &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.
-        auto response = exec(cmdArgs);
-
-        values.insert(values.end(), response.begin(), response.end());
-    }
-
-    return values;
-}
-
-std::vector<nlohmann::json> 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());
-
-    std::vector<nlohmann::json> values;
-
-    if (alias != m_aliases.end()) {
-        auto response = exec(alias->second, args);
-
-        values.insert(values.end(), response.begin(), response.end());
-    } else {
-        auto cmd = m_commandService.find(name);
-
-        if (cmd)
-            values.push_back(exec(*cmd, args));
-        else
-            throw std::invalid_argument("no alias or command named " + name);
-    }
-
-    return values;
-}
-
-void Irccdctl::run(int argc, char **argv)
-{
-    // 1. Read command line arguments.
-    auto 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);
-        else {
-            for (const std::string &dir : path::list(path::PathConfig)) {
-                std::string path = dir + "irccdctl.conf";
-
-                if (fs::exists(path)) {
-                    read(path);
-                    break;
-                }
-            }
-        }
-    } 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("{}: no connection specified"_format(sys::programName()));
-            std::exit(1);
-        }
-
-        m_connection->onDisconnect.connect([this] (auto reason) {
-            log::warning() << "connection lost to irccd: " << reason << std::endl;
-        });
-        m_connection->onConnect.connect([this] (auto info) {
-            log::info() << "connected to irccd "
-                        << info.major << "."
-                        << info.minor << "."
-                        << info.patch << std::endl;
-        });
-        m_connection->onEvent.connect([this] (auto msg) {
-            m_events.push_back(std::move(msg));
-        });
-        m_connection->onMessage.connect([this] (auto msg) {
-            m_messages.push_back(std::move(msg));
-        });
-
-        m_connection->connect(m_address);
-    } else if (argc == 1)
-        help();
-        // NOTREACHED
-
-    // Build a vector of arguments.
-    std::vector<std::string> args;
-
-    for (int i = 0; i < argc; ++i)
-        args.push_back(argv[i]);
-
-    auto commands = exec(args);
-
-    for (const auto &r : commands) {
-        auto name = r.find("response");
-
-        if (name == r.end() || !name->is_string())
-            log::warning() << "unknown irccd response with no response" << std::endl;
-
-        auto it = m_commandService.find(*name);
-
-        it->result(*this, r);
-    }
-}
-
-} // !irccd
--- a/lib/irccd/irccdctl.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-/*
- * irccdctl.hpp -- 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 IRCCD_IRCCDCTL_HPP
-#define IRCCD_IRCCDCTL_HPP
-
-/**
- * \file irccdctl.hpp
- * \brief Base class for irccdctl front end.
- */
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "client.hpp"
-#include "alias.hpp"
-#include "options.hpp"
-#include "service-command.hpp"
-
-#include <json.hpp>
-
-namespace irccd {
-
-class Client;
-
-namespace ini {
-
-class Document;
-class Section;
-
-} // !ini
-
-/**
- * \brief Main irccdctl class.
- */
-class Irccdctl {
-private:
-    // Commands.
-    CommandService m_commandService;
-
-    // Connection handler.
-    std::unique_ptr<Client> m_connection;
-    std::uint32_t m_timeout{30000};
-    net::Address m_address;
-
-    // Aliases.
-    std::map<std::string, Alias> m_aliases;
-
-    // Incoming data.
-    std::vector<nlohmann::json> m_events;
-    std::vector<nlohmann::json> m_messages;
-
-    void usage() const;
-    void help() const;
-
-    // Parse configuration file.
-    void readConnectIp(const ini::Section &sc);
-    void readConnectLocal(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);
-
-    // Parse command line options.
-    void parseConnectIp(const option::Result &options);
-    void parseConnectLocal(const option::Result &options);
-    void parseConnect(const option::Result &options);
-    option::Result parse(int &argc, char **&argv);
-
-public:
-    /**
-     * Get the command service.
-     *
-     * \return the command service
-     */
-    inline CommandService &commandService() noexcept
-    {
-        return m_commandService;
-    }
-
-    /**
-     * Get the client connection to irccd.
-     *
-     * \return the connection
-     */
-    inline const Client &client() const noexcept
-    {
-        return *m_connection;
-    }
-
-    /**
-     * Get the client connection to irccd.
-     *
-     * \return the connection
-     */
-    inline Client &client() noexcept
-    {
-        return *m_connection;
-    }
-
-    /**
-     * Get the next message response with the given id.
-     *
-     * If the response id is not provided, get the next incoming message.
-     *
-     * Otherwise, if the id is provided, all other previous messages will be
-     * discarded.
-     *
-     * \param id the response id (e.g. server-message)
-     * \return the next message
-     * \warning this may skip previous events
-     */
-    IRCCD_EXPORT nlohmann::json waitMessage(const std::string id = "");
-
-    /**
-     * Get the next pending even within the internal timeout.
-     *
-     * \return the next event or empty if not available
-     */
-    IRCCD_EXPORT nlohmann::json waitEvent();
-
-    /**
-     * Execute the given command and wait for its result.
-     *
-     * \param cmd the command
-     * \param args the arguments
-     */
-    IRCCD_EXPORT nlohmann::json exec(const Command &cmd, std::vector<std::string> args);
-
-    /**
-     * Execute the given alias.
-     *
-     * \param alias the alias
-     * \param args the arguments
-     */
-    IRCCD_EXPORT std::vector<nlohmann::json> exec(const Alias &alias, std::vector<std::string> args);
-
-    /**
-     * Resolve the command line arguments.
-     *
-     * \param args the main arguments
-     */
-    IRCCD_EXPORT std::vector<nlohmann::json> exec(std::vector<std::string> args);
-
-    /**
-     * Run the irccdctl front end.
-     *
-     * \param argc the number of arguments
-     * \param argv the arguments
-     */
-    IRCCD_EXPORT void run(int argc, char **argv);
-};
-
-} // !irccd
-
-#endif // !IRCCD_IRCCDCTL_HPP
--- a/lib/irccd/logger.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +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 <atomic>
-#include <cassert>
-#include <cerrno>
-#include <cstring>
-#include <fstream>
-#include <iostream>
-#include <stdexcept>
-#include <streambuf>
-
-#include "logger.hpp"
-#include "system.hpp"
-
-#if defined(HAVE_SYSLOG)
-#  include <syslog.h>
-#endif // !HAVE_SYSLOG
-
-namespace irccd {
-
-namespace log {
-
-namespace {
-
-/*
- * User definable options.
- * ------------------------------------------------------------------
- */
-
-std::atomic<bool> verbose{false};
-std::unique_ptr<Logger> iface{new ConsoleLogger};
-std::unique_ptr<Filter> filter{new Filter};
-
-/*
- * Buffer -- output buffer.
- * ------------------------------------------------------------------
- *
- * This class inherits from std::stringbuf and writes the messages to the
- * specified interface function which is one of info, warning and debug.
- */
-
-class Buffer : public std::stringbuf {
-public:
-    enum Level {
-        Debug,
-        Info,
-        Warning
-    };
-
-private:
-    Level m_level;
-
-    void debug(std::string line)
-    {
-        // Print only in debug mode, the buffer is flushed anyway.
-#if !defined(NDEBUG)
-        iface->debug(filter->preDebug(std::move(line)));
-#else
-        (void)line;
-#endif
-    }
-
-    void info(std::string line)
-    {
-        // Print only if verbose, the buffer will be flushed anyway.
-        if (verbose)
-            iface->info(filter->preInfo(std::move(line)));
-    }
-
-    void warning(std::string line)
-    {
-        iface->warning(filter->preWarning(std::move(line)));
-    }
-
-public:
-    inline Buffer(Level level) noexcept
-        : m_level(level)
-    {
-        assert(level >= Debug && level <= Warning);
-    }
-
-    virtual int sync() override
-    {
-        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);
-
-            switch (m_level) {
-            case Level::Debug:
-                debug(std::move(line));
-                break;
-            case Level::Info:
-                info(std::move(line));
-                break;
-            case Level::Warning:
-                warning(std::move(line));
-                break;
-            default:
-                break;
-            }
-        }
-
-        str(buffer);
-
-        return 0;
-    }
-};
-
-/*
- * Local variables.
- * ------------------------------------------------------------------
- */
-
-// Buffers.
-Buffer bufferInfo{Buffer::Info};
-Buffer bufferWarning{Buffer::Warning};
-Buffer bufferDebug{Buffer::Debug};
-
-// Stream outputs.
-std::ostream streamInfo(&bufferInfo);
-std::ostream streamWarning(&bufferWarning);
-std::ostream streamDebug(&bufferDebug);
-
-} // !namespace
-
-/*
- * ConsoleLogger
- * ------------------------------------------------------------------
- */
-
-void ConsoleLogger::info(const std::string &line)
-{
-    std::cout << line << std::endl;
-}
-
-void ConsoleLogger::warning(const std::string &line)
-{
-    std::cerr << line << std::endl;
-}
-
-void ConsoleLogger::debug(const std::string &line)
-{
-    std::cout << line << std::endl;
-}
-
-/*
- * FileLogger
- * ------------------------------------------------------------------
- */
-
-FileLogger::FileLogger(std::string normal, std::string errors)
-    : m_outputNormal(std::move(normal))
-    , m_outputError(std::move(errors))
-{
-}
-
-void FileLogger::info(const std::string &line)
-{
-    std::ofstream(m_outputNormal, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-void FileLogger::warning(const std::string &line)
-{
-    std::ofstream(m_outputError, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-void FileLogger::debug(const std::string &line)
-{
-    std::ofstream(m_outputNormal, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-/*
- * SilentLogger
- * ------------------------------------------------------------------
- */
-
-void SilentLogger::info(const std::string &)
-{
-}
-
-void SilentLogger::warning(const std::string &)
-{
-}
-
-void SilentLogger::debug(const std::string &)
-{
-}
-
-/*
- * SyslogLogger
- * ------------------------------------------------------------------
- */
-
-#if defined(HAVE_SYSLOG)
-
-SyslogLogger::SyslogLogger()
-{
-    openlog(sys::programName().c_str(), LOG_PID, LOG_DAEMON);
-}
-
-SyslogLogger::~SyslogLogger()
-{
-    closelog();
-}
-
-void SyslogLogger::info(const std::string &line)
-{
-    syslog(LOG_INFO | LOG_USER, "%s", line.c_str());
-}
-
-void SyslogLogger::warning(const std::string &line)
-{
-    syslog(LOG_WARNING | LOG_USER, "%s", line.c_str());
-}
-
-void SyslogLogger::debug(const std::string &line)
-{
-    syslog(LOG_DEBUG | LOG_USER, "%s", line.c_str());
-}
-
-#endif // !HAVE_SYSLOG
-
-/*
- * Functions
- * ------------------------------------------------------------------
- */
-
-void setLogger(std::unique_ptr<Logger> newiface) noexcept
-{
-    assert(newiface);
-
-    iface = std::move(newiface);
-}
-
-void setFilter(std::unique_ptr<Filter> newfilter) noexcept
-{
-    assert(filter);
-
-    filter = std::move(newfilter);
-}
-
-std::ostream &info(const std::string &message)
-{
-    if (!message.empty())
-        streamInfo << message << std::endl;
-
-    return streamInfo;
-}
-
-std::ostream &warning(const std::string &message)
-{
-    if (!message.empty())
-        streamWarning << message << std::endl;
-
-    return streamWarning;
-}
-
-std::ostream &debug(const std::string &message)
-{
-    if (!message.empty())
-        streamDebug << message << std::endl;
-
-    return streamDebug;
-}
-
-bool isVerbose() noexcept
-{
-    return verbose;
-}
-
-void setVerbose(bool mode) noexcept
-{
-    verbose = mode;
-}
-
-} // !log
-
-} // !irccd
--- a/lib/irccd/logger.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-/*
- * logger.hpp -- 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_HPP
-#define IRCCD_LOGGER_HPP
-
-/**
- * \file logger.hpp
- * \brief Logging facilities.
- */
-
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-namespace log {
-
-/*
- * Logger -- abstract logging interface
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Interface to implement new logger mechanisms.
- *
- * Derive from this class and use log::setLogger() to change logging system.
- *
- * \see File
- * \see Console
- * \see Syslog
- * \see Silent
- */
-class Logger {
-public:
-    /**
-     * Default constructor.
-     */
-    Logger() = default;
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~Logger() = default;
-
-    /**
-     * Write a debug message.
-     *
-     * This function is called only if NDEBUG is not defined.
-     *
-     * \param line the data
-     * \see log::debug
-     */
-    virtual void debug(const std::string &line) = 0;
-
-    /**
-     * Write a information message.
-     *
-     * The function is called only if verbose is true.
-     *
-     * \param line the data
-     * \see log::info
-     */
-    virtual void info(const std::string &line) = 0;
-
-    /**
-     * Write an error message.
-     *
-     * This function is always called.
-     *
-     * \param line the data
-     * \see log::warning
-     */
-    virtual void warning(const std::string &line) = 0;
-};
-
-/*
- * Filter -- modify messages before printing
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Filter messages before printing them.
- *
- * Derive from this class and use log::setFilter.
- */
-class Filter {
-public:
-    /**
-     * Default constructor.
-     */
-    Filter() = default;
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~Filter() = default;
-
-    /**
-     * Update the debug message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string preDebug(std::string input) const
-    {
-        return input;
-    }
-
-    /**
-     * Update the information message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string preInfo(std::string input) const
-    {
-        return input;
-    }
-
-    /**
-     * Update the warning message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string preWarning(std::string input) const
-    {
-        return input;
-    }
-};
-
-/*
- * Console -- logs to console
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Logger implementation for console output using std::cout and
- *        std::cerr.
- */
-class ConsoleLogger : public Logger {
-public:
-    IRCCD_EXPORT ConsoleLogger() = default;
-
-    /**
-     * \copydoc Logger::debug
-     */
-    IRCCD_EXPORT void debug(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::info
-     */
-    IRCCD_EXPORT void info(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::warning
-     */
-    IRCCD_EXPORT void warning(const std::string &line) override;
-};
-
-/*
- * File -- logs to a file
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Output to a files.
- */
-class FileLogger : public Logger {
-private:
-    std::string m_outputNormal;
-    std::string m_outputError;
-
-public:
-    /**
-     * Outputs to files.
-     *
-     * \param normal the path to the normal logs
-     * \param errors the path to the errors logs
-     */
-    IRCCD_EXPORT FileLogger(std::string normal, std::string errors);
-
-    /**
-     * \copydoc Logger::debug
-     */
-    IRCCD_EXPORT void debug(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::info
-     */
-    IRCCD_EXPORT void info(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::warning
-     */
-    IRCCD_EXPORT void warning(const std::string &line) override;
-};
-
-/*
- * Silent -- disable all logs
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Use to disable logs.
- *
- * Useful for unit tests when some classes may emits log.
- */
-class SilentLogger : public Logger {
-public:
-    IRCCD_EXPORT SilentLogger() = default;
-
-    /**
-     * \copydoc Logger::debug
-     */
-    IRCCD_EXPORT void debug(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::info
-     */
-    IRCCD_EXPORT void info(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::warning
-     */
-    IRCCD_EXPORT void warning(const std::string &line) override;
-};
-
-/*
- * Syslog -- system logger
- * ------------------------------------------------------------------
- */
-
-#if defined(HAVE_SYSLOG)
-
-/**
- * \brief Implements logger into syslog.
- */
-class SyslogLogger : public Logger {
-public:
-    /**
-     * Open the syslog.
-     */
-    IRCCD_EXPORT SyslogLogger();
-
-    /**
-     * Close the syslog.
-     */
-    IRCCD_EXPORT ~SyslogLogger();
-
-    /**
-     * \copydoc Logger::debug
-     */
-    IRCCD_EXPORT void debug(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::info
-     */
-    IRCCD_EXPORT void info(const std::string &line) override;
-
-    /**
-     * \copydoc Logger::warning
-     */
-    IRCCD_EXPORT void warning(const std::string &line) override;
-};
-
-#endif // !HAVE_SYSLOG
-
-/*
- * Functions
- * ------------------------------------------------------------------
- */
-
-/**
- * Update the logger interface.
- *
- * \pre iface must not be null
- * \param iface the new interface
- */
-IRCCD_EXPORT void setLogger(std::unique_ptr<Logger> iface) noexcept;
-
-/**
- * Set an optional filter.
- *
- * \pre filter must not be null
- * \param filter the filter
- */
-IRCCD_EXPORT void setFilter(std::unique_ptr<Filter> filter) noexcept;
-
-/**
- * Get the stream for informational messages.
- *
- * If message is specified, a new line character is appended.
- *
- * \param message the optional message to write
- * \return the stream
- * \note Has no effect if verbose is set to false.
- */
-IRCCD_EXPORT std::ostream &info(const std::string &message = "");
-
-/**
- * Get the stream for warnings.
- *
- * If message is specified, a new line character is appended.
- *
- * \param message the optional message to write
- * \return the stream
- */
-IRCCD_EXPORT std::ostream &warning(const std::string &message = "");
-
-/**
- * Get the stream for debug messages.
- *
- * If message is specified, a new line character is appended.
- *
- * \param message the optional message to write
- * \return the stream
- * \note Has no effect if compiled in release mode.
- */
-IRCCD_EXPORT std::ostream &debug(const std::string &message = "");
-
-/**
- * Tells if verbose is enabled.
- *
- * \return true if enabled
- */
-IRCCD_EXPORT bool isVerbose() noexcept;
-
-/**
- * Set the verbosity mode.
- *
- * \param mode the new mode
- */
-IRCCD_EXPORT void setVerbose(bool mode) noexcept;
-
-} // !log
-
-} // !irccd
-
-#endif // !IRCCD_LOGGER_HPP
--- a/lib/irccd/mod-directory.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,391 +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 "duktape.hpp"
-#include "fs.hpp"
-#include "mod-directory.hpp"
-#include "mod-irccd.hpp"
-#include "path.hpp"
-#include "plugin-js.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-namespace {
-
-std::string path(duk_context *ctx)
-{
-    duk_push_this(ctx);
-    duk_get_prop_string(ctx, -1, "path");
-
-    if (duk_get_type(ctx, -1) != DUK_TYPE_STRING)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object");
-
-    auto ret = dukx_get_std_string(ctx, -1);
-
-    if (ret.empty())
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path");
-
-    duk_pop_n(ctx, 2);
-
-    return ret;
-}
-
-/*
- * Find an entry recursively (or not) in a directory using a predicate which can
- * be used to test for regular expression, equality.
- *
- * Do not use this function directly, use:
- *
- * - findName
- * - findRegex
- */
-template <typename Pred>
-std::string findPath(const std::string &base, bool recursive, Pred pred)
-{
-    /*
-     * For performance reason, we first iterate over all entries that are
-     * not directories to avoid going deeper recursively if the requested
-     * file is in the current directory.
-     */
-    auto entries = fs::readdir(base);
-
-    for (const auto &entry : entries)
-        if (entry.type != fs::Entry::Dir && pred(entry.name))
-            return base + entry.name;
-
-    if (!recursive)
-        return "";
-
-    for (const auto &entry : entries) {
-        if (entry.type == fs::Entry::Dir) {
-            std::string next = base + entry.name + fs::separator();
-            std::string path = findPath(next, true, pred);
-
-            if (!path.empty())
-                return path;
-        }
-    }
-
-    return "";
-}
-
-/*
- * Helper for finding by equality.
- */
-std::string findName(std::string base, const std::string &pattern, bool recursive)
-{
-    return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
-        return pattern == entryname;
-    });
-}
-
-/*
- * Helper for finding by regular expression
- */
-std::string findRegex(const std::string &base, std::string pattern, bool recursive)
-{
-    std::regex regexp(pattern, std::regex::ECMAScript);
-    std::smatch smatch;
-
-    return findPath(base, recursive, [&] (const std::string &entryname) -> bool {
-        return std::regex_match(entryname, smatch, regexp);
-    });
-}
-
-/*
- * Generic find function for:
- *
- * - Directory.find
- * - Directory.prototype.find
- *
- * The patternIndex is the argument where to test if the argument is a regex or
- * a string.
- */
-duk_ret_t find(duk_context *ctx, std::string base, bool recursive, int patternIndex)
-{
-    base = path::clean(base);
-
-    try {
-        std::string path;
-
-        if (duk_is_string(ctx, patternIndex))
-            path = findName(base, duk_get_string(ctx, patternIndex), recursive);
-        else {
-            // Check if it's a valid RegExp object.
-            duk_get_global_string(ctx, "RegExp");
-            auto isRegex = duk_instanceof(ctx, patternIndex, -1);
-            duk_pop(ctx);
-
-            if (isRegex) {
-                duk_get_prop_string(ctx, patternIndex, "source");
-                auto pattern = duk_to_string(ctx, -1);
-                duk_pop(ctx);
-
-                path = findRegex(base, pattern, recursive);
-            } else
-                duk_error(ctx, DUK_ERR_TYPE_ERROR, "pattern must be a string or a regex expression");
-        }
-
-        if (path.empty())
-            return 0;
-
-        dukx_push_std_string(ctx, path);
-    } catch (const std::exception &ex) {
-        duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-    }
-
-    return 1;
-}
-
-/*
- * Generic remove function for:
- *
- * - Directory.remove
- * - Directory.prototype.remove
- */
-duk_ret_t remove(duk_context *ctx, const std::string &path, bool recursive)
-{
-    if (!fs::isDirectory(path))
-        dukx_throw(ctx, SystemError(EINVAL, "not a directory"));
-
-    if (!recursive) {
-#if defined(_WIN32)
-        ::RemoveDirectory(path.c_str());
-#else
-        ::remove(path.c_str());
-#endif
-    } else
-        fs::rmdir(path.c_str());
-
-    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 if not found.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodFind(duk_context *ctx)
-{
-    return find(ctx, path(ctx), duk_get_boolean(ctx, 1), 0);
-}
-
-/*
- * Method: Directory.remove(recursive)
- * --------------------------------------------------------
- *
- * Synonym of Directory.remove(recursive) but the path is taken from the
- * directory object.
- *
- * Arguments:
- *   - recursive, recursively or not (default: false).
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodRemove(duk_context *ctx)
-{
-    return remove(ctx, path(ctx), duk_get_boolean(ctx, 0));
-}
-
-const duk_function_list_entry methods[] = {
-    { "find",       methodFind,     DUK_VARARGS },
-    { "remove",     methodRemove,   1           },
-    { nullptr,      nullptr,        0           }
-};
-
-/*
- * Directory "static" functions
- * ------------------------------------------------------------------
- */
-
-/*
- * Function: Irccd.Directory(path, flags) [constructor]
- * --------------------------------------------------------
- *
- * Opens and read the directory at the specified path.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - flags, the optional flags (default: 0).
- * Throws:
- *   - Any exception on error
- */
-duk_ret_t constructor(duk_context *ctx)
-{
-    if (!duk_is_constructor_call(ctx))
-        return 0;
-
-    try {
-        std::string path = duk_require_string(ctx, 0);
-        std::int8_t flags = duk_get_uint(ctx, 1);
-
-        if (!fs::isDirectory(path))
-            dukx_throw(ctx, SystemError(EINVAL, "not a directory"));
-
-        std::vector<fs::Entry> list = fs::readdir(path, flags);
-
-        duk_push_this(ctx);
-        duk_push_string(ctx, "count");
-        duk_push_int(ctx, list.size());
-        duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-        duk_push_string(ctx, "path");
-        dukx_push_std_string(ctx, path);
-        duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-        duk_push_string(ctx, "entries");
-        duk_push_array(ctx);
-
-        for (unsigned i = 0; i < list.size(); ++i) {
-            duk_push_object(ctx);
-            dukx_push_std_string(ctx, list[i].name);
-            duk_put_prop_string(ctx, -2, "name");
-            duk_push_int(ctx, list[i].type);
-            duk_put_prop_string(ctx, -2, "type");
-            duk_put_prop_index(ctx, -2, i);
-        }
-
-        duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
-    } catch (const std::exception &ex) {
-        dukx_throw(ctx, SystemError(errno, ex.what()));
-    }
-
-    return 0;
-}
-
-/*
- * Function: Irccd.Directory.find(path, pattern, recursive)
- * --------------------------------------------------------
- *
- * Find an entry by a pattern or a regular expression.
- *
- * Arguments:
- *   - path, the base path,
- *   - pattern, the regular expression or file name,
- *   - recursive, set to true to search recursively (default: false).
- * Returns:
- *   The path to the file or undefined on errors or not found.
- */
-duk_ret_t funcFind(duk_context *ctx)
-{
-    return find(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 2), 1);
-}
-
-/*
- * Function: Irccd.Directory.remove(path, recursive)
- * --------------------------------------------------------
- *
- * Remove the directory optionally recursively.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - recursive, recursively or not (default: false).
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t funcRemove(duk_context *ctx)
-{
-    return remove(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 1));
-}
-
-/*
- * Function: Irccd.Directory.mkdir(path, mode = 0700)
- * --------------------------------------------------------
- *
- * Create a directory specified by path. It will create needed subdirectories
- * just like you have invoked mkdir -p.
- *
- * Arguments:
- *   - path, the path to the directory,
- *   - mode, the mode, not available on all platforms.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t funcMkdir(duk_context *ctx)
-{
-    try {
-        fs::mkdir(
-            duk_require_string(ctx, 0),
-            duk_is_number(ctx, 1) ? duk_get_int(ctx, 1) : 0700
-        );
-    } catch (const std::exception &ex) {
-        dukx_throw(ctx, SystemError(errno, ex.what()));
-    }
-
-    return 0;
-}
-
-const duk_function_list_entry functions[] = {
-    { "find",           funcFind,   DUK_VARARGS },
-    { "mkdir",          funcMkdir,  DUK_VARARGS },
-    { "remove",         funcRemove, DUK_VARARGS },
-    { nullptr,          nullptr,    0           }
-};
-
-const duk_number_list_entry constants[] = {
-    { "Dot",            static_cast<int>(fs::Dot)               },
-    { "DotDot",         static_cast<int>(fs::DotDot)            },
-    { "TypeUnknown",    static_cast<int>(fs::Entry::Unknown)    },
-    { "TypeDir",        static_cast<int>(fs::Entry::Dir)        },
-    { "TypeFile",       static_cast<int>(fs::Entry::File)       },
-    { "TypeLink",       static_cast<int>(fs::Entry::Link)       },
-    { nullptr,          0                                       }
-};
-
-} // !namespace
-
-DirectoryModule::DirectoryModule() noexcept
-    : Module("Irccd.Directory")
-{
-}
-
-void DirectoryModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
-    dukx_push_std_string(plugin->context(), std::string{fs::separator()});
-    duk_put_prop_string(plugin->context(), -2, "separator");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Directory");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-directory.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-directory.hpp -- 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_MOD_DIRECTORY_HPP
-#define IRCCD_MOD_DIRECTORY_HPP
-
-/**
- * \file mod-directory.hpp
- * \brief Irccd.Directory JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Directory JavaScript API.
- * \ingroup modules
- */
-class DirectoryModule : public Module {
-public:
-    /**
-     * Irccd.Directory.
-     */
-    IRCCD_EXPORT DirectoryModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_DIRECTORY_HPP
--- a/lib/irccd/mod-elapsed-timer.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +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.hpp"
-#include "mod-elapsed-timer.hpp"
-#include "plugin-js.hpp"
-
-namespace irccd {
-
-namespace {
-
-const char *Signature("\xff""\xff""irccd-elapsed-timer-ptr");
-
-ElapsedTimer *self(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_push_this(ctx);
-    duk_get_prop_string(ctx, -1, Signature);
-    auto ptr = static_cast<ElapsedTimer *>(duk_to_pointer(ctx, -1));
-    duk_pop_2(ctx);
-
-    if (!ptr)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object");
-
-    return ptr;
-}
-
-/*
- * Method: ElapsedTimer.pause
- * ------------------------------------------------------------------
- *
- * Pause the timer, without resetting the current elapsed time stored.
- */
-duk_ret_t pause(duk_context *ctx)
-{
-    self(ctx)->pause();
-
-    return 0;
-}
-
-/*
- * Method: ElapsedTimer.reset
- * ------------------------------------------------------------------
- *
- * Reset the elapsed time to 0, the status is not modified.
- */
-duk_ret_t reset(duk_context *ctx)
-{
-    self(ctx)->reset();
-
-    return 0;
-}
-
-/*
- * Method: ElapsedTimer.restart
- * ------------------------------------------------------------------
- *
- * Restart the timer without resetting the current elapsed time.
- */
-duk_ret_t restart(duk_context *ctx)
-{
-    self(ctx)->restart();
-
-    return 0;
-}
-
-/*
- * Method: ElapsedTimer.elapsed
- * ------------------------------------------------------------------
- *
- * Get the number of elapsed milliseconds.
- *
- * Returns:
- *   The time elapsed.
- */
-duk_ret_t elapsed(duk_context *ctx)
-{
-    duk_push_uint(ctx, self(ctx)->elapsed());
-
-    return 1;
-}
-
-/*
- * Function: Irccd.ElapsedTimer() [constructor]
- * ------------------------------------------------------------------
- *
- * Construct a new ElapsedTimer object.
- */
-duk_ret_t constructor(duk_context *ctx)
-{
-    duk_push_this(ctx);
-    duk_push_pointer(ctx, new ElapsedTimer);
-    duk_put_prop_string(ctx, -2, Signature);
-    duk_pop(ctx);
-
-    return 0;
-}
-
-/*
- * Function: Irccd.ElapsedTimer() [destructor]
- * ------------------------------------------------------------------
- *
- * Delete the property.
- */
-duk_ret_t destructor(duk_context *ctx)
-{
-    duk_get_prop_string(ctx, 0, Signature);
-    delete static_cast<ElapsedTimer *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-    duk_del_prop_string(ctx, 0, Signature);
-
-    return 0;
-}
-
-const duk_function_list_entry methods[] = {
-    { "elapsed",    elapsed,    0 },
-    { "pause",      pause,      0 },
-    { "reset",      reset,      0 },
-    { "restart",    restart,    0 },
-    { nullptr,      nullptr,    0 }
-};
-
-} // !namespace
-
-ElapsedTimerModule::ElapsedTimerModule() noexcept
-    : Module("Irccd.ElapsedTimer")
-{
-}
-
-void ElapsedTimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 0);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "ElapsedTimer");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-elapsed-timer.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-elapsed-timer.hpp -- 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_MOD_ELAPSED_TIMER_HPP
-#define IRCCD_MOD_ELAPSED_TIMER_HPP
-
-/**
- * \file mod-elapsed-timer.hpp
- * \brief Irccd.ElapsedTimer JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.ElapsedTimer JavaScript API.
- * \ingroup modules
- */
-class ElapsedTimerModule : public Module {
-public:
-    /**
-     * Irccd.ElapsedTimer.
-     */
-    IRCCD_EXPORT ElapsedTimerModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_ELAPSED_TIMER_HPP
--- a/lib/irccd/mod-file.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,693 +0,0 @@
-/*
- * js-file.cpp -- 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 <algorithm>
-#include <array>
-#include <cassert>
-#include <iterator>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-#if defined(HAVE_STAT)
-#  include <sys/types.h>
-#  include <sys/stat.h>
-#endif
-
-#include "fs.hpp"
-#include "mod-file.hpp"
-#include "mod-irccd.hpp"
-#include "plugin-js.hpp"
-
-namespace irccd {
-
-namespace {
-
-const char *Signature("\xff""\xff""irccd-file-ptr");
-const char *Prototype("\xff""\xff""irccd-file-prototype");
-
-#if defined(HAVE_STAT)
-
-/*
- * pushStat
- * ------------------------------------------------------------------
- */
-
-void pushStat(duk_context *ctx, const struct stat &st)
-{
-    StackAssert sa(ctx, 1);
-
-    duk_push_object(ctx);
-
-#if defined(HAVE_STAT_ST_ATIME)
-    duk_push_int(ctx, st.st_atime);
-    duk_put_prop_string(ctx, -2, "atime");
-#endif
-#if defined(HAVE_STAT_ST_BLKSIZE)
-    duk_push_int(ctx, st.st_blksize);
-    duk_put_prop_string(ctx, -2, "blksize");
-#endif
-#if defined(HAVE_STAT_ST_BLOCKS)
-    duk_push_int(ctx, st.st_blocks);
-    duk_put_prop_string(ctx, -2, "blocks");
-#endif
-#if defined(HAVE_STAT_ST_CTIME)
-    duk_push_int(ctx, st.st_ctime);
-    duk_put_prop_string(ctx, -2, "ctime");
-#endif
-#if defined(HAVE_STAT_ST_DEV)
-    duk_push_int(ctx, st.st_dev);
-    duk_put_prop_string(ctx, -2, "dev");
-#endif
-#if defined(HAVE_STAT_ST_GID)
-    duk_push_int(ctx, st.st_gid);
-    duk_put_prop_string(ctx, -2, "gid");
-#endif
-#if defined(HAVE_STAT_ST_INO)
-    duk_push_int(ctx, st.st_ino);
-    duk_put_prop_string(ctx, -2, "ino");
-#endif
-#if defined(HAVE_STAT_ST_MODE)
-    duk_push_int(ctx, st.st_mode);
-    duk_put_prop_string(ctx, -2, "mode");
-#endif
-#if defined(HAVE_STAT_ST_MTIME)
-    duk_push_int(ctx, st.st_mtime);
-    duk_put_prop_string(ctx, -2, "mtime");
-#endif
-#if defined(HAVE_STAT_ST_NLINK)
-    duk_push_int(ctx, st.st_nlink);
-    duk_put_prop_string(ctx, -2, "nlink");
-#endif
-#if defined(HAVE_STAT_ST_RDEV)
-    duk_push_int(ctx, st.st_rdev);
-    duk_put_prop_string(ctx, -2, "rdev");
-#endif
-#if defined(HAVE_STAT_ST_SIZE)
-    duk_push_int(ctx, st.st_size);
-    duk_put_prop_string(ctx, -2, "size");
-#endif
-#if defined(HAVE_STAT_ST_UID)
-    duk_push_int(ctx, st.st_uid);
-    duk_put_prop_string(ctx, -2, "uid");
-#endif
-}
-
-#endif // !HAVE_STAT
-
-// Remove trailing \r for CRLF line style.
-inline std::string clearCr(std::string input)
-{
-    if (input.length() > 0 && input.back() == '\r')
-        input.pop_back();
-
-    return input;
-}
-
-File *self(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_push_this(ctx);
-    duk_get_prop_string(ctx, -1, Signature);
-    auto ptr = static_cast<File *>(duk_to_pointer(ctx, -1));
-    duk_pop_2(ctx);
-
-    if (!ptr)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object");
-
-    return ptr;
-}
-
-/*
- * File methods.
- * ------------------------------------------------------------------
- */
-
-/*
- * Method: File.basename()
- * --------------------------------------------------------
- *
- * Synonym of `Irccd.File.basename(path)` but with the path from the file.
- *
- * duk_ret_turns:
- *   The base name.
- */
-duk_ret_t methodBasename(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, fs::baseName(self(ctx)->path()));
-
-    return 1;
-}
-
-/*
- * Method: File.close()
- * --------------------------------------------------------
- *
- * Force close of the file, automatically called when object is collected.
- */
-duk_ret_t methodClose(duk_context *ctx)
-{
-    self(ctx)->close();
-
-    return 0;
-}
-
-/*
- * Method: File.dirname()
- * --------------------------------------------------------
- *
- * Synonym of `Irccd.File.dirname(path)` but with the path from the file.
- *
- * duk_ret_turns:
- *   The directory name.
- */
-duk_ret_t methodDirname(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, fs::dirName(self(ctx)->path()));
-
-    return 1;
-}
-
-/*
- * Method: File.lines()
- * --------------------------------------------------------
- *
- * Read all lines and return an array.
- *
- * duk_ret_turns:
- *   An array with all lines.
- * Throws
- *   - Any exception on error.
- */
-duk_ret_t methodLines(duk_context *ctx)
-{
-    duk_push_array(ctx);
-
-    std::FILE *fp = self(ctx)->handle();
-    std::string buffer;
-    std::array<char, 128> data;
-    std::int32_t i = 0;
-
-    while (std::fgets(&data[0], data.size(), fp) != nullptr) {
-        buffer += data.data();
-
-        auto pos = buffer.find('\n');
-
-        if (pos != std::string::npos) {
-            dukx_push_std_string(ctx, clearCr(buffer.substr(0, pos)));
-            duk_put_prop_index(ctx, -2, i++);
-
-            buffer.erase(0, pos + 1);
-        }
-    }
-
-    // Maybe an error in the stream.
-    if (std::ferror(fp))
-        dukx_throw(ctx, SystemError());
-
-    // Missing '\n' in end of file.
-    if (!buffer.empty()) {
-        dukx_push_std_string(ctx, clearCr(buffer));
-        duk_put_prop_index(ctx, -2, i++);
-    }
-
-    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).
- * duk_ret_turns:
- *   The string.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodRead(duk_context *ctx)
-{
-    auto file = self(ctx);
-    auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1;
-
-    if (amount == 0 || file->handle() == nullptr)
-        return 0;
-
-    try {
-        std::string data;
-        std::size_t total = 0;
-
-        if (amount < 0) {
-            std::array<char, 128> buffer;
-            std::size_t nread;
-
-            while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle())) > 0) {
-                if (std::ferror(file->handle()))
-                    dukx_throw(ctx, SystemError());
-
-                std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data));
-                total += nread;
-            }
-        } else {
-            data.resize((std::size_t)amount);
-            total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle());
-
-            if (std::ferror(file->handle()))
-                dukx_throw(ctx, SystemError());
-
-            data.resize(total);
-        }
-
-        dukx_push_std_string(ctx, data);
-    } catch (const std::exception &) {
-        dukx_throw(ctx, SystemError());
-    }
-
-    return 1;
-}
-
-/*
- * Method: File.readline()
- * --------------------------------------------------------
- *
- * Read the next line available.
- *
- * duk_ret_turns:
- *   The next line or undefined if eof.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodReadline(duk_context *ctx)
-{
-    std::FILE *fp = self(ctx)->handle();
-    std::string result;
-
-    if (fp == nullptr || std::feof(fp))
-        return 0;
-    for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; )
-        result += (char)ch;
-    if (std::ferror(fp))
-        dukx_throw(ctx, SystemError());
-
-    dukx_push_std_string(ctx, clearCr(result));
-
-    return 1;
-}
-
-/*
- * Method: File.remove()
- * --------------------------------------------------------
- *
- * Synonym of File.remove(path) but with the path from the file.
- *
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodRemove(duk_context *ctx)
-{
-    if (::remove(self(ctx)->path().c_str()) < 0)
-        dukx_throw(ctx, SystemError());
-
-    return 0;
-}
-
-/*
- * Method: File.seek(type, amount)
- * --------------------------------------------------------
- *
- * Sets the position in the file.
- *
- * Arguments:
- *   - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet),
- *   - amount, the new offset.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodSeek(duk_context *ctx)
-{
-    auto fp = self(ctx)->handle();
-    auto type = duk_require_int(ctx, 0);
-    auto amount = duk_require_int(ctx, 1);
-
-    if (fp != nullptr && std::fseek(fp, amount, type) != 0)
-        dukx_throw(ctx, SystemError());
-
-    return 0;
-}
-
-#if defined(HAVE_STAT)
-
-/*
- * Method: File.stat() [optional]
- * --------------------------------------------------------
- *
- * Synonym of File.stat(path) but with the path from the file.
- *
- * duk_ret_turns:
- *   The stat information.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodStat(duk_context *ctx)
-{
-    auto file = self(ctx);
-    struct stat st;
-
-    if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0)
-        dukx_throw(ctx, SystemError());
-    else
-        pushStat(ctx, st);
-
-    return 1;
-}
-
-#endif // !HAVE_STAT
-
-/*
- * Method: File.tell()
- * --------------------------------------------------------
- *
- * Get the actual position in the file.
- *
- * duk_ret_turns:
- *   The position.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodTell(duk_context *ctx)
-{
-    auto fp = self(ctx)->handle();
-    long pos;
-
-    if (fp == nullptr)
-        return 0;
-
-    if ((pos = std::ftell(fp)) == -1L)
-        dukx_throw(ctx, SystemError());
-    else
-        duk_push_int(ctx, pos);
-
-    return 1;
-}
-
-/*
- * Method: File.write(data)
- * --------------------------------------------------------
- *
- * Write some characters to the file.
- *
- * Arguments:
- *   - data, the character to write.
- * duk_ret_turns:
- *   The number of bytes written.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t methodWrite(duk_context *ctx)
-{
-    std::FILE *fp = self(ctx)->handle();
-    std::string data = duk_require_string(ctx, 0);
-
-    if (fp == nullptr)
-        return 0;
-
-    std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp);
-
-    if (std::ferror(fp))
-        dukx_throw(ctx, SystemError());
-
-    duk_push_uint(ctx, nwritten);
-
-    return 1;
-}
-
-const duk_function_list_entry methods[] = {
-    { "basename",   methodBasename, 0 },
-    { "close",      methodClose,    0 },
-    { "dirname",    methodDirname,  0 },
-    { "lines",      methodLines,    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 },
-    { nullptr,      nullptr,        0 }
-};
-
-/*
- * File "static" functions
- * ------------------------------------------------------------------
- */
-
-/*
- * Function: Irccd.File(path, mode) [constructor]
- * --------------------------------------------------------
- *
- * Open a file specified by path with the specified mode.
- *
- * Arguments:
- *   - path, the path to the file,
- *   - mode, the mode string.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t constructor(duk_context *ctx)
-{
-    if (!duk_is_constructor_call(ctx))
-        return 0;
-
-    try {
-        dukx_new_file(ctx, new File(duk_require_string(ctx, 0), duk_require_string(ctx, 1)));
-    } catch (const std::exception &) {
-        dukx_throw(ctx, SystemError());
-    }
-
-    return 0;
-}
-
-/*
- * Function: Irccd.File() [destructor]
- * ------------------------------------------------------------------
- *
- * Delete the property.
- */
-duk_ret_t destructor(duk_context *ctx)
-{
-    duk_get_prop_string(ctx, 0, Signature);
-    delete static_cast<File *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-    duk_del_prop_string(ctx, 0, Signature);
-
-    return 0;
-}
-
-/*
- * Function: Irccd.File.basename(path)
- * --------------------------------------------------------
- *
- * duk_ret_turn the file basename as specified in `basename(3)` C function.
- *
- * Arguments:
- *   - path, the path to the file.
- * duk_ret_turns:
- *   The base name.
- */
-duk_ret_t functionBasename(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, fs::baseName(duk_require_string(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.File.dirname(path)
- * --------------------------------------------------------
- *
- * duk_ret_turn the file directory name as specified in `dirname(3)` C function.
- *
- * Arguments:
- *   - path, the path to the file.
- * duk_ret_turns:
- *   The directory name.
- */
-duk_ret_t functionDirname(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, fs::dirName(duk_require_string(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.File.exists(path)
- * --------------------------------------------------------
- *
- * Check if the file exists.
- *
- * Arguments:
- *   - path, the path to the file.
- * duk_ret_turns:
- *   True if exists.
- * Throws:
- *   - Any exception if we don't have access.
- */
-duk_ret_t functionExists(duk_context *ctx)
-{
-    duk_push_boolean(ctx, fs::exists(duk_require_string(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * function Irccd.File.remove(path)
- * --------------------------------------------------------
- *
- * Remove the file at the specified path.
- *
- * Arguments:
- *   - path, the path to the file.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t functionRemove(duk_context *ctx)
-{
-    if (::remove(duk_require_string(ctx, 0)) < 0)
-        dukx_throw(ctx, SystemError());
-
-    return 0;
-}
-
-#if defined(HAVE_STAT)
-
-/*
- * function Irccd.File.stat(path) [optional]
- * --------------------------------------------------------
- *
- * Get file information at the specified path.
- *
- * Arguments:
- *   - path, the path to the file.
- * duk_ret_turns:
- *   The stat information.
- * Throws:
- *   - Any exception on error.
- */
-duk_ret_t functionStat(duk_context *ctx)
-{
-    struct stat st;
-
-    if (::stat(duk_require_string(ctx, 0), &st) < 0)
-        dukx_throw(ctx, SystemError());
-
-    pushStat(ctx, st);
-
-    return 1;
-}
-
-#endif // !HAVE_STAT
-
-const duk_function_list_entry functions[] = {
-    { "basename",   functionBasename,   1 },
-    { "dirname",    functionDirname,    1 },
-    { "exists",     functionExists,     1 },
-    { "remove",     functionRemove,     1 },
-#if defined(HAVE_STAT)
-    { "stat",       functionStat,       1 },
-#endif
-    { nullptr,      nullptr,            0 }
-};
-
-const duk_number_list_entry constants[] = {
-    { "SeekCur",    SEEK_CUR },
-    { "SeekEnd",    SEEK_END },
-    { "SeekSet",    SEEK_SET },
-    { nullptr,      0        }
-};
-
-} // !namespace
-
-FileModule::FileModule() noexcept
-    : Module("Irccd.File")
-{
-}
-
-void FileModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup(plugin->context(), -1);
-    duk_put_global_string(plugin->context(), Prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "File");
-    duk_pop(plugin->context());
-}
-
-void dukx_new_file(duk_context *ctx, File *fp)
-{
-    assert(ctx);
-    assert(fp);
-
-    StackAssert sa(ctx);
-
-    duk_push_this(ctx);
-    duk_push_pointer(ctx, fp);
-    duk_put_prop_string(ctx, -2, Signature);
-    duk_pop(ctx);
-}
-
-void dukx_push_file(duk_context *ctx, File *fp)
-{
-    assert(ctx);
-    assert(fp);
-
-    StackAssert sa(ctx, 1);
-
-    duk_push_object(ctx);
-    duk_push_pointer(ctx, fp);
-    duk_put_prop_string(ctx, -2, Signature);
-    duk_get_global_string(ctx, Prototype);
-    duk_set_prototype(ctx, -2);
-}
-
-File *dukx_require_file(duk_context *ctx, duk_idx_t index)
-{
-    if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, Signature))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object");
-
-    duk_get_prop_string(ctx, index, Signature);
-    File *file = static_cast<File *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-
-    return file;
-}
-
-} // !irccd
--- a/lib/irccd/mod-file.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-/*
- * mod-file.hpp -- 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_MOD_FILE_HPP
-#define IRCCD_MOD_FILE_HPP
-
-/**
- * \file mod-file.hpp
- * \brief Irccd.File JavaScript API.
- */
-
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <stdexcept>
-#include <string>
-
-#include "duktape.hpp"
-#include "module.hpp"
-
-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;
-
-private:
-    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
-     */
-    inline 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));
-    }
-
-    /**
-     * 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
-     * \param destructor the function to close fp (e.g. std::fclose)
-     */
-    inline File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept
-        : m_stream(fp)
-        , m_destructor(std::move(destructor))
-    {
-        assert(m_destructor != nullptr);
-    }
-
-    /**
-     * Closes the file.
-     */
-    virtual ~File() noexcept
-    {
-        close();
-    }
-
-    /**
-     * Get the path.
-     *
-     * \return the path
-     * \warning empty when constructed from the FILE constructor
-     */
-    inline const std::string &path() const noexcept
-    {
-        return m_path;
-    }
-
-    /**
-     * Get the handle.
-     *
-     * \return the handle or nullptr if the stream was closed
-     */
-    inline std::FILE *handle() noexcept
-    {
-        return m_stream;
-    }
-
-    /**
-     * Force close, can be safely called multiple times.
-     */
-    inline void close() noexcept
-    {
-        if (m_stream) {
-            m_destructor(m_stream);
-            m_stream = nullptr;
-        }
-    }
-};
-
-/**
- * \brief Irccd.File JavaScript API.
- * \ingroup modules
- */
-class FileModule : public Module {
-public:
-    /**
-     * Irccd.File.
-     */
-    IRCCD_EXPORT FileModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-/**
- * Construct the file as this.
- *
- * The object prototype takes ownership of fp and will be deleted once
- * collected.
- *
- * \pre fp != nullptr
- * \param ctx the the context
- * \param fp the file
- */
-IRCCD_EXPORT void dukx_new_file(duk_context *ctx, File *fp);
-
-/**
- * Push a file.
- *
- * \pre fp != nullptr
- * \param ctx the the context
- * \param fp the file
- */
-IRCCD_EXPORT void dukx_push_file(duk_context *ctx, File *fp);
-
-/**
- * Require a file. Raises a JavaScript error if not a File.
- *
- * \param ctx the context
- * \param index the index
- */
-IRCCD_EXPORT File *dukx_require_file(duk_context *ctx, duk_idx_t index);
-
-} // !irccd
-
-#endif // !IRCCD_MOD_FILE_HPP
--- a/lib/irccd/mod-irccd.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,211 +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 <cerrno>
-#include <string>
-#include <unordered_map>
-
-#include "mod-irccd.hpp"
-#include "plugin-js.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-namespace {
-
-const std::unordered_map<std::string, int> errors{
-    { "E2BIG",              E2BIG           },
-    { "EACCES",             EACCES          },
-    { "EADDRINUSE",         EADDRINUSE      },
-    { "EADDRNOTAVAIL",      EADDRNOTAVAIL   },
-    { "EAFNOSUPPORT",       EAFNOSUPPORT    },
-    { "EAGAIN",             EAGAIN          },
-    { "EALREADY",           EALREADY        },
-    { "EBADF",              EBADF           },
-    { "EBADMSG",            EBADMSG         },
-    { "EBUSY",              EBUSY           },
-    { "ECANCELED",          ECANCELED       },
-    { "ECHILD",             ECHILD          },
-    { "ECONNABORTED",       ECONNABORTED    },
-    { "ECONNREFUSED",       ECONNREFUSED    },
-    { "ECONNRESET",         ECONNRESET      },
-    { "EDEADLK",            EDEADLK         },
-    { "EDESTADDRREQ",       EDESTADDRREQ    },
-    { "EDOM",               EDOM            },
-    { "EEXIST",             EEXIST          },
-    { "EFAULT",             EFAULT          },
-    { "EFBIG",              EFBIG           },
-    { "EHOSTUNREACH",       EHOSTUNREACH    },
-    { "EIDRM",              EIDRM           },
-    { "EILSEQ",             EILSEQ          },
-    { "EINPROGRESS",        EINPROGRESS     },
-    { "EINTR",              EINTR           },
-    { "EINVAL",             EINVAL          },
-    { "EIO",                EIO             },
-    { "EISCONN",            EISCONN         },
-    { "EISDIR",             EISDIR          },
-    { "ELOOP",              ELOOP           },
-    { "EMFILE",             EMFILE          },
-    { "EMLINK",             EMLINK          },
-    { "EMSGSIZE",           EMSGSIZE        },
-    { "ENAMETOOLONG",       ENAMETOOLONG    },
-    { "ENETDOWN",           ENETDOWN        },
-    { "ENETRESET",          ENETRESET       },
-    { "ENETUNREACH",        ENETUNREACH     },
-    { "ENFILE",             ENFILE          },
-    { "ENOBUFS",            ENOBUFS         },
-    { "ENODATA",            ENODATA         },
-    { "ENODEV",             ENODEV          },
-    { "ENOENT",             ENOENT          },
-    { "ENOEXEC",            ENOEXEC         },
-    { "ENOLCK",             ENOLCK          },
-    { "ENOLINK",            ENOLINK         },
-    { "ENOMEM",             ENOMEM          },
-    { "ENOMSG",             ENOMSG          },
-    { "ENOPROTOOPT",        ENOPROTOOPT     },
-    { "ENOSPC",             ENOSPC          },
-    { "ENOSR",              ENOSR           },
-    { "ENOSTR",             ENOSTR          },
-    { "ENOSYS",             ENOSYS          },
-    { "ENOTCONN",           ENOTCONN        },
-    { "ENOTDIR",            ENOTDIR         },
-    { "ENOTEMPTY",          ENOTEMPTY       },
-    { "ENOTRECOVERABLE",    ENOTRECOVERABLE },
-    { "ENOTSOCK",           ENOTSOCK        },
-    { "ENOTSUP",            ENOTSUP         },
-    { "ENOTTY",             ENOTTY          },
-    { "ENXIO",              ENXIO           },
-    { "EOPNOTSUPP",         EOPNOTSUPP      },
-    { "EOVERFLOW",          EOVERFLOW       },
-    { "EOWNERDEAD",         EOWNERDEAD      },
-    { "EPERM",              EPERM           },
-    { "EPIPE",              EPIPE           },
-    { "EPROTO",             EPROTO          },
-    { "EPROTONOSUPPORT",    EPROTONOSUPPORT },
-    { "EPROTOTYPE",         EPROTOTYPE      },
-    { "ERANGE",             ERANGE          },
-    { "EROFS",              EROFS           },
-    { "ESPIPE",             ESPIPE          },
-    { "ESRCH",              ESRCH           },
-    { "ETIME",              ETIME           },
-    { "ETIMEDOUT",          ETIMEDOUT       },
-    { "ETXTBSY",            ETXTBSY         },
-    { "EWOULDBLOCK",        EWOULDBLOCK     },
-    { "EXDEV",              EXDEV           }
-};
-
-duk_ret_t constructor(duk_context *ctx)
-{
-    duk_push_this(ctx);
-    duk_push_int(ctx, duk_require_int(ctx, 0));
-    duk_put_prop_string(ctx, -2, "errno");
-    duk_push_string(ctx, duk_require_string(ctx, 1));
-    duk_put_prop_string(ctx, -2, "message");
-    duk_push_string(ctx, "SystemError");
-    duk_put_prop_string(ctx, -2, "name");
-    duk_pop(ctx);
-
-    return 0;
-}
-
-} // !namespace
-
-SystemError::SystemError()
-    : m_errno(errno)
-    , m_message(std::strerror(m_errno))
-{
-}
-
-SystemError::SystemError(int e, std::string message)
-    : m_errno(e)
-    , m_message(std::move(message))
-{
-}
-
-void SystemError::raise(duk_context *ctx) const
-{
-    StackAssert sa(ctx, 0);
-
-    duk_get_global_string(ctx, "Irccd");
-    duk_get_prop_string(ctx, -1, "SystemError");
-    duk_remove(ctx, -2);
-    duk_push_int(ctx, m_errno);
-    dukx_push_std_string(ctx, m_message);
-    duk_new(ctx, 2);
-    duk_throw(ctx);
-}
-
-IrccdModule::IrccdModule() noexcept
-    : Module("Irccd")
-{
-}
-
-void IrccdModule::load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    // Irccd.
-    duk_push_object(plugin->context());
-
-    // Version.
-    duk_push_object(plugin->context());
-    duk_push_int(plugin->context(), IRCCD_VERSION_MAJOR);
-    duk_put_prop_string(plugin->context(), -2, "major");
-    duk_push_int(plugin->context(), IRCCD_VERSION_MINOR);
-    duk_put_prop_string(plugin->context(), -2, "minor");
-    duk_push_int(plugin->context(), IRCCD_VERSION_PATCH);
-    duk_put_prop_string(plugin->context(), -2, "patch");
-    duk_put_prop_string(plugin->context(), -2, "version");
-
-    // Create the SystemError that inherits from Error.
-    duk_push_c_function(plugin->context(), constructor, 2);
-
-    // Put errno codes into the Irccd.SystemError object.
-    for (const auto &pair : errors) {
-        duk_push_int(plugin->context(), pair.second);
-        duk_put_prop_string(plugin->context(), -2, pair.first.c_str());
-    }
-
-    duk_push_object(plugin->context());
-    duk_get_global_string(plugin->context(), "Error");
-    duk_get_prop_string(plugin->context(), -1, "prototype");
-    duk_remove(plugin->context(), -2);
-    duk_set_prototype(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "SystemError");
-
-    // Set Irccd as global.
-    duk_put_global_string(plugin->context(), "Irccd");
-
-    // Store global instance.
-    duk_push_pointer(plugin->context(), &irccd);
-    duk_put_global_string(plugin->context(), "\xff""\xff""irccd-ref");
-}
-
-Irccd &dukx_get_irccd(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_get_global_string(ctx, "\xff""\xff""irccd-ref");
-    Irccd *irccd = static_cast<Irccd *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-
-    return *irccd;
-}
-
-} // !irccd
--- a/lib/irccd/mod-irccd.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * mod-irccd.hpp -- 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_MOD_IRCCD_HPP
-#define IRCCD_MOD_IRCCD_HPP
-
-/**
- * \file mod-irccd.hpp
- * \brief Irccd JavaScript API.
- */
-
-#include <cerrno>
-#include <cstring>
-#include <string>
-
-#include "duktape.hpp"
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Custom JavaScript exception for system error.
- */
-class SystemError {
-private:
-    int m_errno;
-    std::string m_message;
-
-public:
-    /**
-     * Create a system error from the current errno value.
-     */
-    IRCCD_EXPORT SystemError();
-
-    /**
-     * Create a system error with the given errno and message.
-     *
-     * \param e the errno number
-     * \param message the message
-     */
-    IRCCD_EXPORT SystemError(int e, std::string message);
-
-    /**
-     * Raise the SystemError.
-     *
-     * \param ctx the context
-     */
-    IRCCD_EXPORT void raise(duk_context *ctx) const;
-};
-
-/**
- * \brief Irccd JavaScript API.
- * \ingroup modules
- */
-class IrccdModule : public Module {
-public:
-    /**
-     * Irccd.
-     */
-    IRCCD_EXPORT IrccdModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-/**
- * Get irccd instance stored in this context.
- *
- * \param ctx the context
- * \return the irccd reference
- */
-Irccd &dukx_get_irccd(duk_context *ctx);
-
-} // !irccd
-
-#endif // !IRCCD_MOD_IRCCD_HPP
--- a/lib/irccd/mod-logger.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/*
- * mod-logger.cpp -- 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 "mod-logger.hpp"
-#include "mod-plugin.hpp"
-#include "logger.hpp"
-#include "plugin-js.hpp"
-
-namespace irccd {
-
-namespace {
-
-duk_ret_t print(duk_context *ctx, std::ostream &out)
-{
-    out << "plugin " << dukx_get_plugin(ctx)->name() << ": " << duk_require_string(ctx, 0) << std::endl;
-
-    return 0;
-}
-
-/*
- * Function: Irccd.Logger.info(message)
- * --------------------------------------------------------
- *
- * Write a verbose message.
- *
- * Arguments:
- *   - message, the message.
- */
-duk_ret_t info(duk_context *ctx)
-{
-    return print(ctx, log::info());
-}
-
-/*
- * Function: Irccd.Logger.warning(message)
- * --------------------------------------------------------
- *
- * Write a warning message.
- *
- * Arguments:
- *   - message, the warning.
- */
-duk_ret_t warning(duk_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.
- */
-duk_ret_t debug(duk_context *ctx)
-{
-    return print(ctx, log::debug());
-}
-
-const duk_function_list_entry functions[] = {
-    { "info",       info,       1 },
-    { "warning",    warning,    1 },
-    { "debug",      debug,      1 },
-    { nullptr,      nullptr,    0 }
-};
-
-} // !namespace
-
-LoggerModule::LoggerModule() noexcept
-    : Module("Irccd.Logger")
-{
-}
-
-void LoggerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Logger");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-logger.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-logger.hpp -- 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_MOD_LOGGER_HPP
-#define IRCCD_MOD_LOGGER_HPP
-
-/**
- * \file mod-logger.hpp
- * \brief Irccd.Logger JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Logger JavaScript API.
- * \ingroup modules
- */
-class LoggerModule : public Module {
-public:
-    /**
-     * Irccd.Logger.
-     */
-    IRCCD_EXPORT LoggerModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_LOGGER_HPP
--- a/lib/irccd/mod-plugin.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,356 +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.hpp"
-#include "plugin-js.hpp"
-#include "service-plugin.hpp"
-#include "mod-irccd.hpp"
-#include "mod-plugin.hpp"
-
-namespace irccd {
-
-namespace {
-
-const char PluginGlobal[] = "\xff""\xff""irccd-plugin-ptr";
-
-/*
- * wrap
- * ------------------------------------------------------------------
- *
- * Wrap function for these functions because they all takes the same arguments.
- *
- * - load,
- * - reload,
- * - unload.
- */
-template <typename Func>
-duk_idx_t wrap(duk_context *ctx, int nret, Func &&func)
-{
-    std::string name = duk_require_string(ctx, 0);
-
-    try {
-        func(dukx_get_irccd(ctx), name);
-    } catch (const std::out_of_range &ex) {
-        dukx_throw(ctx, ReferenceError(ex.what()));
-    } catch (const std::exception &ex) {
-        dukx_throw(ctx, Error(ex.what()));
-    }
-
-    return nret;
-}
-
-/*
- * set
- * ------------------------------------------------------------------
- *
- * This setter is used to replace the Irccd.Plugin.(config|format) property when
- * the plugin assign a new one.
- *
- * Because the plugin configuration always has higher priority, when a new
- * object is assigned to 'config' or to the 'format' property, the plugin
- * configuration is merged to the assigned one, adding or replacing any values.
- *
- * Example:
- *
- * Plugin 'xyz' does:
- *
- * Irccd.Plugin.config = {
- *      mode: "simple",
- *      level: "123"
- * };
- *
- * The user configuration is:
- *
- * [plugin.xyz]
- * mode = "hard"
- * path = "/var"
- *
- * The final user table looks like this:
- *
- * Irccd.Plugin.config = {
- *      mode: "hard",
- *      level: "123",
- *      path: "/var"
- */
-duk_ret_t set(duk_context *ctx, const char *name)
-{
-    if (!duk_is_object(ctx, 0))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name);
-
-    // Merge old table with new one.
-    duk_get_global_string(ctx, name);
-    duk_enum(ctx, -1, 0);
-
-    while (duk_next(ctx, -1, true))
-        duk_put_prop(ctx, 0);
-
-    // Pop enum and old table.
-    duk_pop_2(ctx);
-
-    // Replace the old table with the new assigned one.
-    duk_put_global_string(ctx, name);
-
-    return 0;
-}
-
-/*
- * get
- * ------------------------------------------------------------------
- *
- * Get the Irccd.Plugin.(config|format) property.
- */
-duk_ret_t get(duk_context *ctx, const char *name)
-{
-    duk_get_global_string(ctx, name);
-
-    return 1;
-}
-
-/*
- * setConfig
- * ------------------------------------------------------------------
- *
- * Wrap setter for Irccd.Plugin.config property.
- */
-duk_ret_t setConfig(duk_context *ctx)
-{
-    return set(ctx, JsPlugin::ConfigProperty);
-}
-
-/*
- * getConfig
- * ------------------------------------------------------------------
- *
- * Wrap getter for Irccd.Plugin.config property.
- */
-duk_ret_t getConfig(duk_context *ctx)
-{
-    return get(ctx, JsPlugin::ConfigProperty);
-}
-
-/*
- * setFormat
- * ------------------------------------------------------------------
- *
- * Wrap setter for Irccd.Plugin.format property.
- */
-duk_ret_t setFormat(duk_context *ctx)
-{
-    return set(ctx, JsPlugin::FormatProperty);
-}
-
-/*
- * getFormat
- * ------------------------------------------------------------------
- *
- * Wrap getter for Irccd.Plugin.format property.
- */
-duk_ret_t getFormat(duk_context *ctx)
-{
-    return get(ctx, JsPlugin::FormatProperty);
-}
-
-/*
- * Function: Irccd.Plugin.info([name])
- * ------------------------------------------------------------------
- *
- * Get information about a plugin.
- *
- * The returned object as the following properties:
- *
- * - name: (string) the plugin identifier,
- * - author: (string) the author,
- * - license: (string) the license,
- * - summary: (string) a short description,
- * - version: (string) the version
- *
- * Arguments:
- *   - name, the plugin identifier, if not specified the current plugin is
- *     selected.
- * Returns:
- *   The plugin information or undefined if the plugin was not found.
- */
-duk_idx_t info(duk_context *ctx)
-{
-    std::shared_ptr<Plugin> plugin;
-
-    if (duk_get_top(ctx) >= 1)
-        plugin = dukx_get_irccd(ctx).plugins().get(duk_require_string(ctx, 0));
-    else
-        plugin = dukx_get_plugin(ctx);
-
-    if (!plugin)
-        return 0;
-
-    duk_push_object(ctx);
-    dukx_push_std_string(ctx, plugin->name());
-    duk_put_prop_string(ctx, -2, "name");
-    dukx_push_std_string(ctx, plugin->author());
-    duk_put_prop_string(ctx, -2, "author");
-    dukx_push_std_string(ctx, plugin->license());
-    duk_put_prop_string(ctx, -2, "license");
-    dukx_push_std_string(ctx, plugin->summary());
-    duk_put_prop_string(ctx, -2, "summary");
-    dukx_push_std_string(ctx, plugin->version());
-    duk_put_prop_string(ctx, -2, "version");
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Plugin.list()
- * ------------------------------------------------------------------
- *
- * Get the list of plugins, the array returned contains all plugin names.
- *
- * Returns:
- *   The list of all plugin names.
- */
-duk_idx_t list(duk_context *ctx)
-{
-    dukx_push_array(ctx, dukx_get_irccd(ctx).plugins().list(), [] (auto ctx, auto plugin) {
-        dukx_push_std_string(ctx, plugin->name());
-    });
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Plugin.load(name)
- * ------------------------------------------------------------------
- *
- * Load a plugin by name. This function will search through the standard
- * directories.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-duk_idx_t load(duk_context *ctx)
-{
-    return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-        irccd.plugins().load(name);
-    });
-}
-
-/*
- * Function: Irccd.Plugin.reload(name)
- * ------------------------------------------------------------------
- *
- * Reload a plugin by name.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-duk_idx_t reload(duk_context *ctx)
-{
-    return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-        irccd.plugins().reload(name);
-    });
-}
-
-/*
- * Function: Irccd.Plugin.unload(name)
- * ------------------------------------------------------------------
- *
- * Unload a plugin by name.
- *
- * Arguments:
- *   - name, the plugin identifier.
- * Throws:
- *   - Error on errors,
- *   - ReferenceError if the plugin was not found.
- */
-duk_idx_t unload(duk_context *ctx)
-{
-    return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) {
-        irccd.plugins().unload(name);
-    });
-}
-
-const duk_function_list_entry functions[] = {
-    { "info",   info,       DUK_VARARGS     },
-    { "list",   list,       0               },
-    { "load",   load,       1               },
-    { "reload", reload,     1               },
-    { "unload", unload,     1               },
-    { nullptr,  nullptr,    0               }
-};
-
-} // !namespace
-
-PluginModule::PluginModule() noexcept
-    : Module("Irccd.Plugin")
-{
-}
-
-void PluginModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_push_pointer(plugin->context(), new std::shared_ptr<JsPlugin>(plugin));
-    duk_put_global_string(plugin->context(), PluginGlobal);
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-
-    // 'config' property.
-    duk_push_string(plugin->context(), "config");
-    duk_push_c_function(plugin->context(), getConfig, 0);
-    duk_push_c_function(plugin->context(), setConfig, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
-
-    // 'format' property.
-    duk_push_string(plugin->context(), "format");
-    duk_push_c_function(plugin->context(), getFormat, 0);
-    duk_push_c_function(plugin->context(), setFormat, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
-
-    duk_put_prop_string(plugin->context(), -2, "Plugin");
-    duk_pop(plugin->context());
-}
-
-void PluginModule::unload(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_push_global_object(plugin->context());
-    duk_get_prop_string(plugin->context(), -1, PluginGlobal);
-    delete static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(plugin->context(), -1));
-    duk_pop(plugin->context());
-    duk_del_prop_string(plugin->context(), -1, PluginGlobal);
-    duk_pop(plugin->context());
-}
-
-std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_get_global_string(ctx, PluginGlobal);
-    auto plugin = static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-
-    return *plugin;
-}
-
-} // !irccd
--- a/lib/irccd/mod-plugin.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * mod-plugin.hpp -- 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_MOD_PLUGIN_HPP
-#define IRCCD_MOD_PLUGIN_HPP
-
-/**
- * \file mod-plugin.hpp
- * \brief Irccd.Plugin JavaScript API.
- */
-
-#include "duktape.hpp"
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Plugin JavaScript API.
- * \ingroup modules
- */
-class PluginModule : public Module {
-public:
-    /**
-     * Irccd.Plugin.
-     */
-    IRCCD_EXPORT PluginModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-
-    /**
-     * \copydoc Module::unload
-     */
-    IRCCD_EXPORT void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-/**
- * Access the plugin stored in this context.
- *
- * \param ctx the context
- * \return the plugin
- */
-std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx);
-
-} // !irccd
-
-#endif // !IRCCD_MOD_PLUGIN_HPP
--- a/lib/irccd/mod-server.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,580 +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 <cassert>
-#include <sstream>
-#include <unordered_map>
-
-#include "irccd.hpp"
-#include "mod-irccd.hpp"
-#include "mod-server.hpp"
-#include "plugin-js.hpp"
-#include "server.hpp"
-#include "service-server.hpp"
-
-namespace irccd {
-
-namespace {
-
-const char *Signature("\xff""\xff""irccd-server-ptr");
-const char *Prototype("\xff""\xff""irccd-server-prototype");
-
-std::shared_ptr<Server> self(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_push_this(ctx);
-    duk_get_prop_string(ctx, -1, Signature);
-    auto ptr = duk_to_pointer(ctx, -1);
-    duk_pop_2(ctx);
-
-    if (!ptr)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object");
-
-    return *static_cast<std::shared_ptr<Server> *>(ptr);
-}
-
-/*
- * Method: Server.cmode(channel, mode)
- * ------------------------------------------------------------------
- *
- * Change a channel mode.
- *
- * Arguments:
- *   - channel, the channel,
- *   - mode, the mode.
- */
-duk_ret_t cmode(duk_context *ctx)
-{
-    self(ctx)->cmode(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.cnotice(channel, message)
- * ------------------------------------------------------------------
- *
- * Send a channel notice.
- *
- * Arguments:
- *   - channel, the channel,
- *   - message, the message.
- */
-duk_ret_t cnotice(duk_context *ctx)
-{
-    self(ctx)->cnotice(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.info()
- * ------------------------------------------------------------------
- *
- * Get the server information as an object containing the following properties:
- *
- * name: the server unique name
- * host: the host name
- * port: the port number
- * ssl: true if using ssl
- * sslVerify: true if ssl was verified
- * channels: an array of all channels
- */
-duk_ret_t info(duk_context *ctx)
-{
-    auto server = self(ctx);
-
-    duk_push_object(ctx);
-    dukx_push_std_string(ctx, server->name());
-    duk_put_prop_string(ctx, -2, "name");
-    dukx_push_std_string(ctx, server->host());
-    duk_put_prop_string(ctx, -2, "host");
-    duk_push_int(ctx, server->port());
-    duk_put_prop_string(ctx, -2, "port");
-    duk_push_boolean(ctx, server->flags() & Server::Ssl);
-    duk_put_prop_string(ctx, -2, "ssl");
-    duk_push_boolean(ctx, server->flags() & Server::SslVerify);
-    duk_put_prop_string(ctx, -2, "sslVerify");
-    dukx_push_std_string(ctx, server->commandCharacter());
-    duk_put_prop_string(ctx, -2, "commandChar");
-    dukx_push_std_string(ctx, server->realname());
-    duk_put_prop_string(ctx, -2, "realname");
-    dukx_push_std_string(ctx, server->nickname());
-    duk_put_prop_string(ctx, -2, "nickname");
-    dukx_push_std_string(ctx, server->username());
-    duk_put_prop_string(ctx, -2, "username");
-    dukx_push_array(ctx, server->channels(), [&] (auto ctx, auto channel) {
-        dukx_push_std_string(ctx, channel);
-    });
-    duk_put_prop_string(ctx, -2, "channels");
-
-    return 1;
-}
-
-/*
- * Method: Server.invite(target, channel)
- * ------------------------------------------------------------------
- *
- * Invite someone to a channel.
- *
- * Arguments:
- *   - target, the target to invite,
- *   - channel, the channel.
- */
-duk_ret_t invite(duk_context *ctx)
-{
-    self(ctx)->invite(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.join(channel, password = undefined)
- * ------------------------------------------------------------------
- *
- * Join a channel with an optional password.
- *
- * Arguments:
- *   - channel, the channel to join,
- *   - password, the password or undefined to not use.
- */
-duk_ret_t join(duk_context *ctx)
-{
-    self(ctx)->join(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.kick(target, channel, reason = undefined)
- * ------------------------------------------------------------------
- *
- * Kick someone from a channel.
- *
- * Arguments:
- *   - target, the target to kick,
- *   - channel, the channel,
- *   - reason, the optional reason or undefined to not set.
- */
-duk_ret_t kick(duk_context *ctx)
-{
-    self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), dukx_get_std_string(ctx, 2));
-
-    return 0;
-}
-
-/*
- * Method: Server.me(target, message)
- * ------------------------------------------------------------------
- *
- * Send a CTCP Action.
- *
- * Arguments:
- *   - target, the target or a channel,
- *   - message, the message.
- */
-duk_ret_t me(duk_context *ctx)
-{
-    self(ctx)->me(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.message(target, message)
- * ------------------------------------------------------------------
- *
- * Send a message.
- *
- * Arguments:
- *   - target, the target or a channel,
- *   - message, the message.
- */
-duk_ret_t message(duk_context *ctx)
-{
-    self(ctx)->message(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.mode(mode)
- * ------------------------------------------------------------------
- *
- * Change your mode.
- *
- * Arguments:
- *   - mode, the new mode.
- */
-duk_ret_t mode(duk_context *ctx)
-{
-    self(ctx)->mode(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Method: Server.names(channel)
- * ------------------------------------------------------------------
- *
- * Get the list of names from a channel.
- *
- * Arguments:
- *   - channel, the channel.
- */
-duk_ret_t names(duk_context *ctx)
-{
-    self(ctx)->names(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Method: Server.nick(nickname)
- * ------------------------------------------------------------------
- *
- * Change the nickname.
- *
- * Arguments:
- *   - nickname, the nickname.
- */
-duk_ret_t nick(duk_context *ctx)
-{
-    self(ctx)->setNickname(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Method: Server.notice(target, message)
- * ------------------------------------------------------------------
- *
- * Send a private notice.
- *
- * Arguments:
- *   - target, the target,
- *   - message, the notice message.
- */
-duk_ret_t notice(duk_context *ctx)
-{
-    self(ctx)->notice(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.part(channel, reason = undefined)
- * ------------------------------------------------------------------
- *
- * Leave a channel.
- *
- * Arguments:
- *   - channel, the channel to leave,
- *   - reason, the optional reason, keep undefined for portability.
- */
-duk_ret_t part(duk_context *ctx)
-{
-    self(ctx)->part(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.send(raw)
- * ------------------------------------------------------------------
- *
- * Send a raw message to the IRC server.
- *
- * Arguments:
- *   - raw, the raw message (without terminators).
- */
-duk_ret_t send(duk_context *ctx)
-{
-    self(ctx)->send(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Method: Server.topic(channel, topic)
- * ------------------------------------------------------------------
- *
- * Change a channel topic.
- *
- * Arguments:
- *   - channel, the channel,
- *   - topic, the new topic.
- */
-duk_ret_t topic(duk_context *ctx)
-{
-    self(ctx)->topic(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    return 0;
-}
-
-/*
- * Method: Server.whois(target)
- * ------------------------------------------------------------------
- *
- * Get whois information.
- *
- * Arguments:
- *   - target, the target.
- */
-duk_ret_t whois(duk_context *ctx)
-{
-    self(ctx)->whois(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Method: Server.toString()
- * ------------------------------------------------------------------
- *
- * Convert the object to std::string, convenience for adding the object
- * as property key.
- *
- * duk_ret_turns:
- *   The server name (unique).
- */
-duk_ret_t toString(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, self(ctx)->name());
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Server(params) [constructor]
- * ------------------------------------------------------------------
- *
- * Construct a new server.
- *
- * Params must be filled with the following properties:
- *
- * name: the name,
- * host: the host,
- * ipv6: true to use ipv6,      (Optional: default false)
- * port: the port number,       (Optional: default 6667)
- * password: the password,      (Optional: default none)
- * channels: array of channels  (Optiona: default empty)
- * ssl: true to use ssl,        (Optional: default false)
- * sslVerify: true to verify    (Optional: default true)
- * nickname: "nickname",        (Optional, default: irccd)
- * username: "user name",       (Optional, default: irccd)
- * realname: "real name",       (Optional, default: IRC Client Daemon)
- * commandChar: "!",            (Optional, the command char, default: "!")
- */
-duk_ret_t constructor(duk_context *ctx)
-{
-    if (!duk_is_constructor_call(ctx))
-        return 0;
-
-    duk_check_type(ctx, 0, DUK_TYPE_OBJECT);
-
-    try {
-        auto json = duk_json_encode(ctx, 0);
-        auto s = Server::fromJson(nlohmann::json::parse(json));
-
-        duk_push_this(ctx);
-        duk_push_pointer(ctx, new std::shared_ptr<Server>(std::move(s)));
-        duk_put_prop_string(ctx, -2, Signature);
-        duk_pop(ctx);
-    } catch (const std::exception &ex) {
-        duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-    }
-
-    return 0;
-}
-
-/*
- * Function: Irccd.Server() [destructor]
- * ------------------------------------------------------------------
- *
- * Delete the property.
- */
-duk_ret_t destructor(duk_context *ctx)
-{
-    duk_get_prop_string(ctx, 0, Signature);
-    delete static_cast<std::shared_ptr<Server> *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-    duk_del_prop_string(ctx, 0, Signature);
-
-    return 0;
-}
-
-/*
- * Function: Irccd.Server.add(s)
- * ------------------------------------------------------------------
- *
- * Register a new server to the irccd instance.
- *
- * Arguments:
- *   - s, the server to add.
- */
-duk_ret_t add(duk_context *ctx)
-{
-    dukx_get_irccd(ctx).servers().add(dukx_require_server(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Function: Irccd.Server.find(name)
- * ------------------------------------------------------------------
- *
- * Find a server by name.
- *
- * Arguments:
- *   - name, the server name
- * duk_ret_turns:
- *   The server object or undefined if not found.
- */
-duk_ret_t find(duk_context *ctx)
-{
-    auto server = dukx_get_irccd(ctx).servers().get(duk_require_string(ctx, 0));
-
-    if (!server)
-        return 0;
-
-    dukx_push_server(ctx, server);
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Server.list()
- * ------------------------------------------------------------------
- *
- * Get the map of all loaded servers.
- *
- * duk_ret_turns:
- *   An object with string-to-servers pairs.
- */
-duk_ret_t list(duk_context *ctx)
-{
-    duk_push_object(ctx);
-
-    for (const auto &server : dukx_get_irccd(ctx).servers().servers()) {
-        dukx_push_server(ctx, server);
-        duk_put_prop_string(ctx, -2, server->name().c_str());
-    }
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Server.remove(name)
- * ------------------------------------------------------------------
- *
- * Remove a server from the irccd instance. You can pass the server object since
- * it's coercible to a string.
- *
- * Arguments:
- *   - name the server name.
- */
-duk_ret_t remove(duk_context *ctx)
-{
-    dukx_get_irccd(ctx).servers().remove(duk_require_string(ctx, 0));
-
-    return 0;
-}
-
-const duk_function_list_entry 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           },
-    { nullptr,      nullptr,    0           }
-};
-
-const duk_function_list_entry functions[] = {
-    { "add",        add,        1           },
-    { "find",       find,       1           },
-    { "list",       list,       0           },
-    { "remove",     remove,     1           },
-    { nullptr,      nullptr,    0           }
-};
-
-} // !namespace
-
-ServerModule::ServerModule() noexcept
-    : Module("Irccd.Server")
-{
-}
-
-void ServerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 1);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup_top(plugin->context());
-    duk_put_global_string(plugin->context(), Prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Server");
-    duk_pop(plugin->context());
-}
-
-void dukx_push_server(duk_context *ctx, std::shared_ptr<Server> server)
-{
-    assert(ctx);
-    assert(server);
-
-    StackAssert sa(ctx, 1);
-
-    duk_push_object(ctx);
-    duk_push_pointer(ctx, new std::shared_ptr<Server>(std::move(server)));
-    duk_put_prop_string(ctx, -2, Signature);
-    duk_get_global_string(ctx, Prototype);
-    duk_set_prototype(ctx, -2);
-}
-
-std::shared_ptr<Server> dukx_require_server(duk_context *ctx, duk_idx_t index)
-{
-    if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, Signature))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object");
-
-    duk_get_prop_string(ctx, index, Signature);
-    auto file = *static_cast<std::shared_ptr<Server> *>(duk_to_pointer(ctx, -1));
-    duk_pop(ctx);
-
-    return file;
-}
-
-} // !irccd
--- a/lib/irccd/mod-server.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * mod-server.hpp -- 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_MOD_SERVER_HPP
-#define IRCCD_MOD_SERVER_HPP
-
-/**
- * \file mod-server.hpp
- * \brief Irccd.Server JavaScript API.
- */
-
-#include "duktape.hpp"
-#include "module.hpp"
-#include "server.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Server JavaScript API.
- * \ingroup modules
- */
-class ServerModule : public Module {
-public:
-    /**
-     * Irccd.Server.
-     */
-    IRCCD_EXPORT ServerModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-/**
- * Push a server.
- *
- * \pre server != nullptr
- * \param ctx the context
- * \param server the server
- */
-IRCCD_EXPORT void dukx_push_server(duk_context *ctx, std::shared_ptr<Server> server);
-
-/**
- * Require a server. Raise a JavaScript error if not a Server.
- *
- * \param ctx the context
- * \param index the index
- * \return the server
- */
-IRCCD_EXPORT std::shared_ptr<Server> dukx_require_server(duk_context *ctx, duk_idx_t index);
-
-} // !irccd
-
-#endif // !IRCCD_JS_SERVER_HPP
--- a/lib/irccd/mod-system.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +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 <chrono>
-#include <cstdlib>
-#include <thread>
-
-#include "sysconfig.hpp"
-
-#if defined(HAVE_POPEN)
-#  include <cstdio>
-#endif
-
-#include "mod-file.hpp"
-#include "mod-irccd.hpp"
-#include "mod-system.hpp"
-#include "plugin-js.hpp"
-#include "system.hpp"
-
-namespace irccd {
-
-namespace {
-
-/*
- * Function: Irccd.System.env(key)
- * ------------------------------------------------------------------
- *
- * Get an environment system variable.
- *
- * Arguments:
- *   - key, the environment variable.
- * Returns:
- *   The value.
- */
-duk_ret_t env(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, sys::env(dukx_get_std_string(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.System.exec(cmd)
- * ------------------------------------------------------------------
- *
- * Execute a system command.
- *
- * Arguments:
- *   - cmd, the command to execute.
- */
-duk_ret_t exec(duk_context *ctx)
-{
-    std::system(duk_get_string(ctx, 0));
-
-    return 0;
-}
-
-/*
- * Function: Irccd.System.home()
- * ------------------------------------------------------------------
- *
- * Get the operating system user's home.
- *
- * Returns:
- *   The user home directory.
- */
-duk_ret_t home(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, sys::home());
-
-    return 1;
-}
-
-/*
- * Function: Irccd.System.name()
- * ------------------------------------------------------------------
- *
- * Get the operating system name.
- *
- * Returns:
- *   The system name.
- */
-duk_ret_t name(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, sys::name());
-
-    return 1;
-}
-
-#if defined(HAVE_POPEN)
-
-/*
- * Function: Irccd.System.popen(cmd, mode) [optional]
- * ------------------------------------------------------------------
- *
- * Wrapper for popen(3) if the function is available.
- *
- * Arguments:
- *   - cmd, the command to execute,
- *   - mode, the mode (e.g. "r").
- * Returns:
- *   A Irccd.File object.
- * Throws
- *   - Irccd.SystemError on failures.
- */
-duk_ret_t popen(duk_context *ctx)
-{
-    auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
-
-    if (fp == nullptr)
-        dukx_throw(ctx, SystemError());
-
-    dukx_push_file(ctx, 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.
- */
-duk_ret_t sleep(duk_context *ctx)
-{
-    std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0)));
-
-    return 0;
-}
-
-/*
- * Function: Irccd.System.ticks()
- * ------------------------------------------------------------------
- *
- * Get the number of milliseconds since irccd was started.
- *
- * Returns:
- *   The number of milliseconds.
- */
-duk_ret_t ticks(duk_context *ctx)
-{
-    duk_push_int(ctx, sys::ticks());
-
-    return 1;
-}
-
-/*
- * Function: Irccd.System.usleep(delay)
- * ------------------------------------------------------------------
- *
- * Sleep the main loop for the specific delay in microseconds.
- */
-duk_ret_t usleep(duk_context *ctx)
-{
-    std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0)));
-
-    return 0;
-}
-
-/*
- * Function: Irccd.System.uptime()
- * ------------------------------------------------------------------
- *
- * Get the system uptime.
- *
- * Returns:
- *   The system uptime.
- */
-duk_ret_t uptime(duk_context *ctx)
-{
-    duk_push_int(ctx, sys::uptime());
-
-    return 0;
-}
-
-/*
- * Function: Irccd.System.version()
- * ------------------------------------------------------------------
- *
- * Get the operating system version.
- *
- * Returns:
- *   The system version.
- */
-duk_ret_t version(duk_context *ctx)
-{
-    dukx_push_std_string(ctx, sys::version());
-
-    return 1;
-}
-
-const duk_function_list_entry 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 },
-    { nullptr,      nullptr,    0 }
-};
-
-} // !namespace
-
-SystemModule::SystemModule() noexcept
-    : Module("Irccd.System")
-{
-}
-
-void SystemModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "System");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-system.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-system.hpp -- 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_MOD_SYSTEM_HPP
-#define IRCCD_MOD_SYSTEM_HPP
-
-/**
- * \file mod-system.hpp
- * \brief Irccd.System JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.System JavaScript API.
- * \ingroup modules
- */
-class SystemModule : public Module {
-public:
-    /**
-     * Irccd.System.
-     */
-    IRCCD_EXPORT SystemModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_SYSTEM_HPP
--- a/lib/irccd/mod-timer.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +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 <format.h>
-
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "mod-irccd.hpp"
-#include "mod-timer.hpp"
-#include "mod-plugin.hpp"
-#include "plugin-js.hpp"
-#include "timer.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-namespace {
-
-const char *Signature("\xff""\xff""irccd-timer-ptr");
-const char *CallbackTable("\xff""\xff""irccd-timer-callbacks");
-
-void handleSignal(std::weak_ptr<JsPlugin> ptr, std::string key)
-{
-    auto plugin = ptr.lock();
-
-    if (!plugin)
-        return;
-
-    auto &irccd = dukx_get_irccd(plugin->context());
-
-    irccd.post([plugin, key] (Irccd &) {
-        StackAssert sa(plugin->context());
-
-        duk_get_global_string(plugin->context(), CallbackTable);
-        duk_get_prop_string(plugin->context(), -1, key.c_str());
-        duk_remove(plugin->context(), -2);
-
-        if (duk_is_callable(plugin->context(), -1)) {
-            if (duk_pcall(plugin->context(), 0) != 0)
-                log::warning("plugin {}: {}"_format(plugin->name(), dukx_exception(plugin->context(), -1).stack));
-            else
-                duk_pop(plugin->context());
-        } else
-            duk_pop(plugin->context());
-    });
-}
-
-std::shared_ptr<Timer> self(duk_context *ctx)
-{
-    StackAssert sa(ctx);
-
-    duk_push_this(ctx);
-    duk_get_prop_string(ctx, -1, Signature);
-    auto ptr = duk_to_pointer(ctx, -1);
-    duk_pop_2(ctx);
-
-    if (!ptr)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object");
-
-    return *static_cast<std::shared_ptr<Timer> *>(ptr);
-}
-
-/*
- * Method: Timer.start()
- * --------------------------------------------------------
- *
- * Start the timer. If the timer is already started the method is a no-op.
- */
-duk_ret_t start(duk_context *ctx)
-{
-    auto timer = self(ctx);
-
-    if (!timer->isRunning())
-        timer->start();
-
-    return 0;
-}
-
-/*
- * Method: Timer.stop()
- * --------------------------------------------------------
- *
- * Stop the timer.
- */
-duk_ret_t stop(duk_context *ctx)
-{
-    auto timer = self(ctx);
-
-    if (timer->isRunning())
-        timer->stop();
-
-    return 0;
-}
-
-const duk_function_list_entry methods[] = {
-    { "start",  start,      0 },
-    { "stop",   stop,       0 },
-    { nullptr,  nullptr,    0 }
-};
-
-/*
- * Function: Irccd.Timer(type, delay, callback) [constructor]
- * --------------------------------------------------------
- *
- * Create a new timer object.
- *
- * Arguments:
- *   - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat),
- *   - delay, the interval in milliseconds,
- *   - callback, the function to call.
- */
-duk_ret_t constructor(duk_context *ctx)
-{
-    // Check parameters.
-    auto type = duk_require_int(ctx, 0);
-    auto delay = duk_require_int(ctx, 1);
-
-    if (type < static_cast<int>(TimerType::Single) || type > static_cast<int>(TimerType::Repeat))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type");
-    if (delay < 0)
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given");
-    if (!duk_is_callable(ctx, 2))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function");
-
-    // Construct the timer in 'this'.
-    auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay);
-    auto hash = std::to_string(reinterpret_cast<std::uintptr_t>(timer.get()));
-
-    timer->onSignal.connect(std::bind(handleSignal, std::weak_ptr<JsPlugin>(dukx_get_plugin(ctx)), hash));
-
-    duk_push_this(ctx);
-    duk_push_pointer(ctx, new std::shared_ptr<Timer>(std::move(timer)));
-    duk_put_prop_string(ctx, -2, Signature);
-    duk_push_string(ctx, hash.c_str());
-    duk_put_prop_string(ctx, -2, "\xff""\xff""timer-key");
-    duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
-        StackAssert sa(ctx);
-
-        duk_get_prop_string(ctx, 0, "\xff""\xff""timer-key");
-        auto hash = duk_get_string(ctx, -1);
-        duk_pop(ctx);
-        duk_get_prop_string(ctx, 0, Signature);
-        static_cast<std::shared_ptr<Timer> *>(duk_to_pointer(ctx, -1))->get()->stop();
-        delete static_cast<std::shared_ptr<Timer> *>(duk_to_pointer(ctx, -1));
-        duk_pop(ctx);
-        duk_get_global_string(ctx, CallbackTable);
-        duk_del_prop_string(ctx, -1, hash);
-        duk_pop(ctx);
-        log::debug("plugin: timer destroyed");
-
-        return 0;
-    }, 1);
-    duk_set_finalizer(ctx, -2);
-
-    // Save a callback function into the callback table.
-    duk_get_global_string(ctx, CallbackTable);
-    duk_dup(ctx, 2);
-    duk_put_prop_string(ctx, -2, hash.c_str());
-    duk_pop(ctx);
-
-    return 0;
-}
-
-const duk_number_list_entry constants[] = {
-    { "Single",     static_cast<int>(TimerType::Single) },
-    { "Repeat",     static_cast<int>(TimerType::Repeat) },
-    { nullptr,      0                                   }
-};
-
-} // !namespace
-
-TimerModule::TimerModule() noexcept
-    : Module("Irccd.Timer")
-{
-}
-
-void TimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 3);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Timer");
-    duk_pop(plugin->context());
-    duk_push_object(plugin->context());
-    duk_put_global_string(plugin->context(), CallbackTable);
-}
-
-} // !irccd
--- a/lib/irccd/mod-timer.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-timer.hpp -- 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_MOD_TIMER_HPP
-#define IRCCD_MOD_TIMER_HPP
-
-/**
- * \file mod-timer.hpp
- * \brief Irccd.Timer JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Timer JavaScript API.
- * \ingroup modules
- */
-class TimerModule : public Module {
-public:
-    /**
-     * Irccd.Timer.
-     */
-    IRCCD_EXPORT TimerModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_TIMER_HPP
--- a/lib/irccd/mod-unicode.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +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 "duktape.hpp"
-#include "mod-unicode.hpp"
-#include "plugin-js.hpp"
-#include "unicode.hpp"
-
-namespace irccd {
-
-namespace {
-
-/*
- * Function: Irccd.Unicode.isDigit(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the digit category.
- */
-duk_ret_t isDigit(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::isdigit(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isLetter(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the letter category.
- */
-duk_ret_t isLetter(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::isalpha(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isLower(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is lower case.
- */
-duk_ret_t isLower(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::islower(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isSpace(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is in the space category.
- */
-duk_ret_t isSpace(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::isspace(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isTitle(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is title case.
- */
-duk_ret_t isTitle(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::istitle(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Unicode.isUpper(code)
- * --------------------------------------------------------
- *
- * Arguments:
- *   - code, the code point.
- * Returns:
- *   True if the code is upper case.
- */
-duk_ret_t isUpper(duk_context *ctx)
-{
-    duk_push_boolean(ctx, unicode::isupper(duk_get_int(ctx, 0)));
-
-    return 1;
-}
-
-const duk_function_list_entry functions[] = {
-    { "isDigit",        isDigit,    1 },
-    { "isLetter",       isLetter,   1 },
-    { "isLower",        isLower,    1 },
-    { "isSpace",        isSpace,    1 },
-    { "isTitle",        isTitle,    1 },
-    { "isUpper",        isUpper,    1 },
-    { nullptr,          nullptr,    0 }
-};
-
-} // !namespace
-
-UnicodeModule::UnicodeModule() noexcept
-    : Module("Irccd.Unicode")
-{
-}
-
-void UnicodeModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Unicode");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-unicode.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-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_MOD_UNICODE_HPP
-#define IRCCD_MOD_UNICODE_HPP
-
-/**
- * \file mod-unicode.hpp
- * \brief Irccd.Unicode JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Unicode JavaScript API.
- * \ingroup modules
- */
-class UnicodeModule : public Module {
-public:
-    /**
-     * Irccd.Unicode.
-     */
-    IRCCD_EXPORT UnicodeModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_UNICODE_HPP
--- a/lib/irccd/mod-util.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +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 "mod-util.hpp"
-#include "plugin-js.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace {
-
-/**
- * Read parameters for Irccd.Util.format function, the object is defined as
- * following:
- *
- * {
- *   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: ...
- * }
- */
-util::Substitution getSubstitution(duk_context *ctx, int index)
-{
-    util::Substitution params;
-
-    if (!duk_is_object(ctx, index))
-        return params;
-
-    dukx_enumerate(ctx, index, 0, true, [&] (duk_context *) {
-        if (dukx_get_std_string(ctx, -2) == "date")
-            params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000);
-        else
-            params.keywords.insert({dukx_get_std_string(ctx, -2), dukx_get_std_string(ctx, -1)});
-    });
-
-    return params;
-}
-
-/*
- * Function: Irccd.Util.format(text, parameters)
- * --------------------------------------------------------
- *
- * Format a string with templates.
- *
- * Arguments:
- *   - input, the text to update,
- *   - params, the parameters.
- * Returns:
- *   The converted text.
- */
-duk_ret_t format(duk_context *ctx)
-{
-    try {
-        dukx_push_std_string(ctx, util::format(dukx_get_std_string(ctx, 0), getSubstitution(ctx, 1)));
-    } catch (const std::exception &ex) {
-        dukx_throw(ctx, SyntaxError(ex.what()));
-    }
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Util.splituser(ident)
- * --------------------------------------------------------
- *
- * Return the nickname part from a full username.
- *
- * Arguments:
- *   - ident, the full identity.
- * Returns:
- *   The nickname.
- */
-duk_ret_t splituser(duk_context *ctx)
-{
-    auto target = duk_require_string(ctx, 0);
-    char nick[32] = {0};
-
-    irc_target_get_nick(target, nick, sizeof (nick) -1);
-    duk_push_string(ctx, nick);
-
-    return 1;
-}
-
-/*
- * Function: Irccd.Util.splithost(ident)
- * --------------------------------------------------------
- *
- * Return the hostname part from a full username.
- *
- * Arguments:
- *   - ident, the full identity.
- * Returns:
- *   The hostname.
- */
-duk_ret_t splithost(duk_context *ctx)
-{
-    auto target = duk_require_string(ctx, 0);
-    char host[32] = {0};
-
-    irc_target_get_host(target, host, sizeof (host) -1);
-    duk_push_string(ctx, host);
-
-    return 1;
-}
-
-const duk_function_list_entry functions[] = {
-    { "format",     format,     DUK_VARARGS },
-    { "splituser",  splituser,  1           },
-    { "splithost",  splithost,  1           },
-    { nullptr,      nullptr,    0           }
-};
-
-} // !namespace
-
-UtilModule::UtilModule() noexcept
-    : Module("Irccd.Util")
-{
-}
-
-void UtilModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Util");
-    duk_pop(plugin->context());
-}
-
-} // !irccd
--- a/lib/irccd/mod-util.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * mod-util.hpp -- 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_MOD_UTIL_HPP
-#define IRCCD_MOD_UTIL_HPP
-
-/**
- * \file mod-util.hpp
- * \brief Irccd.Util JavaScript API.
- */
-
-#include "module.hpp"
-
-namespace irccd {
-
-/**
- * \brief Irccd.Util JavaScript API.
- * \ingroup modules
- */
-class UtilModule : public Module {
-public:
-    /**
-     * Irccd.Util.
-     */
-    IRCCD_EXPORT UtilModule() noexcept;
-
-    /**
-     * \copydoc Module::load
-     */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_MOD_UTIL_HPP
--- a/lib/irccd/module.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/*
- * module.hpp -- JavaScript API module
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_MODULE_HPP
-#define IRCCD_MODULE_HPP
-
-/**
- * \file module.hpp
- * \brief JavaScript API module.
- */
-
-/**
- * \defgroup modules JavaScript modules
- * \brief Modules for the JavaScript API.
- */
-
-#include <cassert>
-#include <memory>
-
-#include "sysconfig.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-class Irccd;
-class JsPlugin;
-
-/**
- * \brief JavaScript API module.
- */
-class Module {
-private:
-    std::string m_name;
-
-public:
-    /**
-     * Default constructor.
-     *
-     * \pre !name.empty()
-     */
-    inline Module(std::string name) noexcept
-        : m_name(std::move(name))
-    {
-        assert(!m_name.empty());
-    }
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~Module() = default;
-
-    /**
-     * Get the module name.
-     *
-     * \return the name
-     */
-    inline const std::string &name() const noexcept
-    {
-        return m_name;
-    }
-
-    /**
-     * Load the module into the JavaScript plugin.
-     *
-     * \param irccd the irccd instance
-     * \param plugin the plugin
-     */
-    virtual void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
-    {
-        util::unused(irccd, plugin);
-    }
-
-    /**
-     * Unload the module from the JavaScript plugin.
-     *
-     * \param irccd the irccd instance
-     * \param plugin the plugin
-     */
-    virtual void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
-    {
-        util::unused(irccd, plugin);
-    }
-};
-
-} // !irccd
-
-#endif // !IRCCD_MODULE_HPP
--- a/lib/irccd/net.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3725 +0,0 @@
-/*
- * net.hpp -- portable C++ socket wrapper
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_NET_HPP
-#define IRCCD_NET_HPP
-
-/**
- * \file net.hpp
- * \brief Networking
- * \author David Demelier <markand@malikania.fr>
- */
-
-/**
- * \defgroup net-module-tcp Network TCP support
- * \brief TCP support in the networking module.
- */
-
-/**
- * \defgroup net-module-udp Network UDP support
- * \brief UDP support in networking module.
- */
-
-/**
- * \defgroup net-module-tls Network TLS support
- * \brief TLS support in networking module.
- */
-
-/**
- * \defgroup net-module-addresses Network predefined addresses
- * \brief Predefined addresses for sockets
- */
-
-/**
- * \defgroup net-module-options Network predefined options
- * \brief Predefined options for sockets
- */
-
-/**
- * \defgroup net-module-backends Network predefined backends
- * \brief Predefined backends for Listener
- */
-
-/**
- * \defgroup net-module-resolv Network resolver
- * \brief Resolv functions
- */
-
-/**
- * \page Networking Networking
- *
- *   - \subpage net-options
- *   - \subpage net-concepts
- */
-
-/**
- * \page net-options User options
- *
- * The user may set the following variables before compiling these files:
- *
- * # General options
- *
- * - **NET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
- *   automatically calls init function and finish functions.
- *
- * - **NET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL
- *   library.
- *
- * - **NET_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
- *   ssl::init and ssl::finish.
- *
- * # General compatibility options.
- *
- * The following options are auto detected but you can override them if you
- * want.
- *
- * - **NET_HAVE_INET_PTON**: (bool) Set to 1 if you have inet_pton function.
- *   True for all platforms and Windows
- *   if _WIN32_WINNT is greater or equal to 0x0600.
- *
- * - **NET_HAVE_INET_NTOP**: (bool) Same as above.
- *
- * **Note:** On Windows, it is highly encouraged to set _WIN32_WINNT to at least
- * 0x0600 on MinGW.
- *
- * # 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.
- *
- * - **NET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows
- *   if _WIN32_WINNT is set to 0x0600 or greater.
- * - **NET_HAVE_KQUEUE**: Defined on all BSD and Apple.
- * - **NET_HAVE_EPOLL**: Defined on Linux only.
- * - **NET_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       |
- */
-
-/**
- * \page net-concepts Concepts
- *
- *   - \subpage net-concept-backend
- *   - \subpage net-concept-option
- *   - \subpage net-concept-stream
- *   - \subpage net-concept-datagram
- */
-
-/**
- * \page net-concept-backend Backend (Concept)
- *
- * A backend is an interface for the Listener class. It is primarily designed to
- * be the most suitable for the host environment.
- *
- * The backend must be default constructible, it is highly encouraged to be move
- * constructible.
- *
- * This concepts requires the following functions:
- *
- * # name
- *
- * Get the backend name, informational only.
- *
- * ## Synopsis
- *
- * ````
- * std::string name() const;
- * ````
- *
- * ## Returns
- *
- * The backend name.
- *
- * # set
- *
- * Set one or more condition for the given handle.
- *
- * ## Synopsis
- *
- * ````
- * void set(const ListenerTable &table, Handle handle, Condition condition,
- * bool add);
- * ````
- *
- * ## Arguments
- *
- *   - **table**: the current table of sockets,
- *   - **handle**: the handle to set,
- *   - **condition**: the condition to add (may be OR'ed),
- *   - **add**: hint set to true if the handle is not currently registered.
- *
- * # unset
- *
- * Unset one or more condition for the given handle.
- *
- * ## Synopsis
- *
- * ````
- * void unset(const ListenerTable &table, Handle handle, Condition condition,
- * bool remove);
- * ````
- *
- * ## Arguments
- *
- *   - **table**: the current table of sockets,
- *   - **handle**: the handle to update,
- *   - **condition**: the condition to remove (may be OR'ed),
- *   - **remove**: hint set to true if the handle will be completely removed.
- *
- * # wait
- *
- * Wait for multiple sockets to be ready.
- *
- * ## Synopsis
- *
- * ````
- * std::vector<ListenerStatus> wait(const ListenerTable &table, int ms);
- * ````
- *
- * ## Arguments
- *
- *   - **table**: the current table,
- *   - **ms**: the number to wait in milliseconds, negative means forever.
- *
- * ## Returns
- *
- * The list of sockets ready paired to their condition.
- */
-
-/**
- * \page net-concept-option Option (Concept)
- *
- * An option can be set or get from a socket.
- *
- * If an operation is not available, provides the function but throws an
- * exception with ENOSYS message.
- *
- * This concepts requires the following functions:
- *
- * # Option (constructor)
- *
- * At least one default constructor must be present.
- *
- * ## Synopsis
- *
- * ````
- * Option() noexcept;
- * ````
- *
- * # set
- *
- * Set the option.
- *
- * ## Synopsis
- *
- * ````
- * template <typename Address>
- * void set(Socket &sc) const;
- * ````
- *
- * ## Arguments
- *
- *   - **sc**: the socket.
- *
- * # get
- *
- * Get an option, T can be any type.
- *
- * ## Synopsis
- *
- * ````
- * template <typename Address>
- * T get(Socket &sc) const;
- * ````
- *
- * ## Arguments
- *
- *   - **sc**: the socket.
- *
- * ## Returns
- *
- * The value.
- */
-
-/**
- * \page net-concept-stream Stream (Concept)
- *
- * This concepts requires the following functions:
- *
- * # type
- *
- * Return the type of socket, usually `SOCK_STREAM`.
- *
- * ## Synopsis
- *
- * ````
- * int type() const noexcept;
- * ````
- *
- * ## Returns
- *
- * The type of socket.
- *
- * # connect
- *
- * Connect to the given address.
- *
- * ## Synopsis
- *
- * ````
- * void connect(const sockaddr *address, socklen_t length); // (0)
- * void connect(const Address &address); // 1 (Optional)
- * ````
- *
- * ## Arguments
- *
- *   - **address**: the address,
- *   - **length**: the address length.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- *
- * # accept
- *
- * Accept a new client.
- *
- * If no pending connection is available and operation would block, the
- * implementation must throw WouldBlockError. Any other error can be thrown
- * otherwise a valid socket must be returned.
- *
- * ## Synopsis
- *
- * ````
- * Socket accept();
- * ````
- *
- * ## Returns
- *
- * The new socket.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- *
- * # recv
- *
- * Receive data.
- *
- * ## Synopsis
- *
- * ````
- * unsigned recv(void *data, unsigned length);
- * ````
- *
- * ## Arguments
- *
- *   - **data**: the destination buffer,
- *   - **length**: the destination buffer length.
- *
- * ## Returns
- *
- * The number of bytes sent.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- *
- * # send
- *
- * Send data.
- *
- * ## Synopsis
- *
- * ````
- * unsigned send(const void *data, unsigned length);
- * ````
- *
- * ## Arguments
- *
- *   - **data**: the data to send,
- *   - **length**: the data length.
- *
- * ## Returns
- *
- * The number of bytes sent.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- */
-
-/**
- * \page net-concept-datagram Datagram (Concept)
- *
- * This concepts requires the following functions:
- *
- * # type
- *
- * Return the type of socket, usually `SOCK_DGRAM`.
- *
- * ## Synopsis
- *
- * ````
- * int type() const noexcept;
- * ````
- *
- * ## Returns
- *
- * The type of socket.
- *
- * # recvfrom
- *
- * Receive data.
- *
- * ## Synopsis
- *
- * ````
- * unsigned recvfrom(void *data, unsigned length, sockaddr *address,
- *     socklen_t *addrlen);
- * unsigned recvfrom(void *data, unsigned length, Address *source)
- * ````
- *
- * ## Arguments
- *
- *   - **data**: the data,
- *   - **length**: the length,
- *   - **address**: the source address,
- *   - **addrlen**: the source address in/out length.
- *
- * ## Returns
- *
- * The number of bytes received.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- *
- * # sendto
- *
- * ````
- * unsigned sendto(const void *data, unsigned length, const sockaddr *address,
- *     socklen_t addrlen);
- * unsigned sendto(const void *data, unsigned length, const Address &address);
- * ````
- *
- * ## Arguments
- *
- *   - **data**: the data to send,
- *   - **length**: the data length,
- *   - **address**: the destination address,
- *   - **addrlen**: the destination address length.
- *
- * ## Returns
- *
- * The number of bytes sent.
- *
- * ## Throws
- *
- *   - net::WouldBlockError: if the operation would block,
- *   - net::Error: on other error.
- */
-
-/*
- * Headers to include.
- * ------------------------------------------------------------------
- */
-
-/*
- * Include Windows headers before because it brings _WIN32_WINNT if not
- * specified by the user.
- */
-#if defined(_WIN32)
-#   include <winsock2.h>
-#   include <ws2tcpip.h>
-#else
-#   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
-
-#include <algorithm>
-#include <atomic>
-#include <cassert>
-#include <cerrno>
-#include <chrono>
-#include <climits>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#if !defined(NET_NO_SSL)
-
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/ssl.h>
-
-#endif // !NET_NO_SSL
-
-/*
- * Determine which I/O multiplexing implementations are available.
- * ------------------------------------------------------------------
- *
- * May define the following:
- *
- *   - NET_HAVE_EPOLL
- *   - NET_HAVE_KQUEUE
- *   - NET_HAVE_POLL
- */
-
-#if defined(_WIN32)
-#   if _WIN32_WINNT >= 0x0600 && !defined(NET_HAVE_POLL)
-#       define NET_HAVE_POLL
-#   endif
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
-#   if !defined(NET_HAVE_KQUEUE)
-#       define NET_HAVE_KQUEUE
-#   endif
-#   if !defined(NET_HAVE_POLL)
-#       define NET_HAVE_POLL
-#   endif
-#elif defined(__linux__)
-#   if !defined(NET_HAVE_EPOLL)
-#       define NET_HAVE_EPOLL
-#   endif
-#   if !defined(NET_HAVE_POLL)
-#       define NET_HAVE_POLL
-#   endif
-#endif
-
-/*
- * Compatibility macros.
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Tells if inet_pton is available
- */
-#if !defined(NET_HAVE_INET_PTON)
-#   if defined(_WIN32)
-#       if _WIN32_WINNT >= 0x0600
-#           define NET_HAVE_INET_PTON
-#       endif
-#   else
-#       define NET_HAVE_INET_PTON
-#   endif
-#endif
-
-/**
- * \brief Tells if inet_ntop is available
- */
-#if !defined(NET_HAVE_INET_NTOP)
-#   if defined(_WIN32)
-#       if _WIN32_WINNT >= 0x0600
-#           define NET_HAVE_INET_NTOP
-#       endif
-#   else
-#       define NET_HAVE_INET_NTOP
-#   endif
-#endif
-
-/*
- * Define NET_DEFAULT_BACKEND.
- * ------------------------------------------------------------------
- *
- * Define the default I/O multiplexing implementation to use if not specified.
- */
-
-/**
- * \brief Defines the default backend
- */
-#if defined(_WIN32)
-#   if !defined(NET_DEFAULT_BACKEND)
-#       if defined(NET_HAVE_POLL)
-#           define NET_DEFAULT_BACKEND Poll
-#       else
-#           define NET_DEFAULT_BACKEND Select
-#       endif
-#   endif
-#elif defined(__linux__)
-#   include <sys/epoll.h>
-
-#   if !defined(NET_DEFAULT_BACKEND)
-#       define NET_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(NET_DEFAULT_BACKEND)
-#       define NET_DEFAULT_BACKEND Kqueue
-#   endif
-#else
-#   if !defined(NET_DEFAULT_BACKEND)
-#       define NET_DEFAULT_BACKEND Select
-#   endif
-#endif
-
-#if defined(NET_HAVE_POLL) && !defined(_WIN32)
-#    include <poll.h>
-#endif
-
-namespace irccd {
-
-/**
- * The network namespace.
- */
-namespace net {
-
-/*
- * Portables types.
- * ------------------------------------------------------------------
- *
- * The following types are defined differently between Unix and Windows.
- */
-
-#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.
- */
-
-#if defined(_WIN32)
-
-/**
- * Socket creation failure or invalidation.
- */
-const Handle Invalid{INVALID_SOCKET};
-
-/**
- * Socket operation failure.
- */
-const int Failure{SOCKET_ERROR};
-
-#else
-
-/**
- * Socket creation failure or invalidation.
- */
-const Handle Invalid{-1};
-
-/**
- * Socket operation failure.
- */
-const int Failure{-1};
-
-#endif
-
-/**
- * Close the socket library.
- */
-inline void finish() noexcept
-{
-#if defined(_WIN32)
-    WSACleanup();
-#endif
-}
-
-/**
- * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you
- * don't need to call this
- * function manually.
- */
-inline void init() noexcept
-{
-#if defined(_WIN32)
-    static std::atomic<bool> initialized;
-    static std::mutex mutex;
-
-    std::lock_guard<std::mutex> lock(mutex);
-
-    if (!initialized) {
-        initialized = true;
-
-        WSADATA wsa;
-        WSAStartup(MAKEWORD(2, 2), &wsa);
-
-        /*
-         * If NET_NO_AUTO_INIT is not set then the user must also call finish
-         * himself.
-         */
-#if !defined(NET_NO_AUTO_INIT)
-        atexit(finish);
-#endif
-    }
-#endif
-}
-
-/**
- * Get the last system error.
- *
- * \param errn the error number (errno or WSAGetLastError)
- * \return the error
- */
-inline 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
-}
-
-/**
- * Get the last socket system error. The error is set from errno or from
- * WSAGetLastError on Windows.
- *
- * \return a string message
- */
-inline std::string error()
-{
-#if defined(_WIN32)
-    return error(WSAGetLastError());
-#else
-    return error(errno);
-#endif
-}
-
-#if !defined(NET_NO_SSL)
-
-/**
- * \brief SSL namespace
- */
-namespace ssl {
-
-/**
- * \enum Method
- * \brief Which OpenSSL method to use.
- */
-enum Method {
-    Tlsv1,      //!< TLS v1.2 (recommended)
-    Sslv3       //!< SSLv3
-};
-
-/**
- * Initialize the OpenSSL library. Except if you defined NET_NO_AUTO_SSL_INIT,
- * you don't need to call this function manually.
- */
-inline void init() noexcept
-{
-    static std::atomic<bool> initialized;
-    static std::mutex mutex;
-
-    std::lock_guard<std::mutex> lock(mutex);
-
-    if (!initialized) {
-        initialized = true;
-
-        SSL_library_init();
-        SSL_load_error_strings();
-        OpenSSL_add_all_algorithms();
-
-#if !defined(NET_NO_AUTO_SSL_INIT)
-        atexit(finish);
-#endif
-    }
-}
-
-/**
- * Close the OpenSSL library.
- */
-inline void finish() noexcept
-{
-    ERR_free_strings();
-}
-
-} // !ssl
-
-#endif // !NET_NO_SSL
-
-/*
- * Error class.
- * ------------------------------------------------------------------
- *
- * This is the main exception thrown on socket operations.
- */
-
-/**
- * \brief Base class for sockets error.
- */
-class Error : public std::exception {
-private:
-    std::string m_message;
-
-public:
-    /**
-     * Construct the error using the specified error from the system.
-     *
-     * \param code the error code
-     * \warning the code must be a Winsock error or errno on Unix
-     */
-    inline Error(int code) noexcept
-        : m_message(error(code))
-    {
-    }
-
-    /**
-     * Construct the error using the custom message.
-     *
-     * \param message the message
-     */
-    inline Error(std::string message) noexcept
-        : m_message(std::move(message))
-    {
-    }
-
-    /**
-     * Construct the error using the last message from the system.
-     */
-    inline Error() noexcept
-#if defined(_WIN32)
-        : Error(WSAGetLastError())
-#else
-        : Error(errno)
-#endif
-    {
-    }
-
-    /**
-     * Get the error (only the error content).
-     *
-     * \return the error
-     */
-    const char *what() const noexcept override
-    {
-        return m_message.c_str();
-    }
-};
-
-/**
- * \brief Timeout occured.
- */
-class TimeoutError : public std::exception {
-public:
-    /**
-     * Get the error message.
-     *
-     * \return the message
-     */
-    const char *what() const noexcept override
-    {
-        return std::strerror(ETIMEDOUT);
-    }
-};
-
-/**
- * \brief Operation would block.
- */
-class WouldBlockError : public std::exception {
-public:
-    /**
-     * Get the error message.
-     *
-     * \return the message
-     */
-    const char *what() const noexcept override
-    {
-        return std::strerror(EWOULDBLOCK);
-    }
-};
-
-/**
- * \brief Operation requires sending data to complete.
- */
-class WantWriteError : public std::exception {
-public:
-    /**
-     * Get the error message.
-     *
-     * \return the message
-     */
-    const char *what() const noexcept override
-    {
-        return "operation requires writing to complete";
-    }
-};
-
-/**
- * \brief Operation requires reading data to complete.
- */
-class WantReadError : public std::exception {
-public:
-    /**
-     * Get the error message.
-     *
-     * \return the message
-     */
-    const char *what() const noexcept override
-    {
-        return "operation requires read to complete";
-    }
-};
-
-/*
- * Condition enum
- * ------------------------------------------------------------------
- *
- * Defines if we must wait for reading or writing.
- */
-
-/**
- * \enum Condition
- * \brief Define the required condition for the socket.
- */
-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
- */
-inline 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
- */
-inline 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
- */
-inline 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
- */
-inline 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;
-}
-
-/**
- * \brief Generic socket address storage.
- * \ingroup net-module-addresses
- */
-class Address {
-private:
-    sockaddr_storage m_storage;
-    socklen_t m_length;
-
-public:
-    /**
-     * Construct empty address.
-     */
-    inline Address() noexcept
-        : m_storage{}
-        , m_length(0)
-    {
-    }
-
-    /**
-     * Construct address from existing one.
-     *
-     * \pre address != nullptr
-     * \param address the address
-     * \param length the address length
-     */
-    inline Address(const sockaddr *address, socklen_t length) noexcept
-        : m_length(length)
-    {
-        assert(address);
-
-        std::memcpy(&m_storage, address, length);
-    }
-
-    /**
-     * Get the underlying address.
-     *
-     * \return the address
-     */
-    inline const sockaddr *get() const noexcept
-    {
-        return reinterpret_cast<const sockaddr *>(&m_storage);
-    }
-
-    /**
-     * Overloaded function
-     *
-     * \return the address
-     */
-    inline sockaddr *get() noexcept
-    {
-        return reinterpret_cast<sockaddr *>(&m_storage);
-    }
-
-    /**
-     * Get the underlying address as the given type (e.g sockaddr_in).
-     *
-     * \return the address reference
-     */
-    template <typename T>
-    inline const T &as() const noexcept
-    {
-        return reinterpret_cast<const T &>(m_storage);
-    }
-
-    /**
-     * Overloaded function
-     *
-     * \return the address reference
-     */
-    template <typename T>
-    inline T &as() noexcept
-    {
-        return reinterpret_cast<T &>(m_storage);
-    }
-
-    /**
-     * Get the underlying address length.
-     *
-     * \return the length
-     */
-    inline socklen_t length() const noexcept
-    {
-        return m_length;
-    }
-
-    /**
-     * Get the address domain.
-     *
-     * \return the domain
-     */
-    inline int domain() const noexcept
-    {
-        return m_storage.ss_family;
-    }
-};
-
-/**
- * \brief Address iterator.
- * \ingroup net-module-addresses
- * \see resolve
- *
- * This iterator can be used to try to connect to an host.
- *
- * When you use resolve with unspecified domain or socket type, the function may
- * retrieve several different addresses that you can iterate over to try to
- * connect to.
- *
- * Example:
- *
- * ````cpp
- * SocketTcp sc;
- * AddressIterator end, it = resolve("hostname.test", "80");
- *
- * while (!connected_condition && it != end)
- *   sc.connect(it->address(), it->length());
- * ````
- *
- * When an iterator equals to a default constructed iterator, it is considered
- * not dereferenceable.
- */
-class AddressIterator : public std::iterator<std::forward_iterator_tag, Address> {
-private:
-    std::vector<Address> m_addresses;
-    std::size_t m_index{0};
-
-public:
-    /**
-     * Construct a null iterator.
-     *
-     * The default constructed iterator is not dereferenceable.
-     */
-    inline AddressIterator() noexcept = default;
-
-    /**
-     * Construct an address iterator with a set of addresses.
-     *
-     * \pre index < m_addresses.size()
-     * \param addresses the addresses
-     * \param index the first index
-     */
-    inline AddressIterator(std::vector<Address> addresses, std::size_t index = 0) noexcept
-        : m_addresses(std::move(addresses))
-        , m_index(index)
-    {
-        assert(index < m_addresses.size());
-    }
-
-    /**
-     * Get the generic address.
-     *
-     * \pre this is dereferenceable
-     * \return the generic address
-     */
-    inline const Address &operator*() const noexcept
-    {
-        assert(m_index <= m_addresses.size());
-
-        return m_addresses[m_index];
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre this is dereferenceable
-     * \return the generic address
-     */
-    inline Address &operator*() noexcept
-    {
-        assert(m_index <= m_addresses.size());
-
-        return m_addresses[m_index];
-    }
-
-    /**
-     * Get the generic address.
-     *
-     * \pre this is dereferenceable
-     * \return the generic address
-     */
-    inline const Address *operator->() const noexcept
-    {
-        assert(m_index <= m_addresses.size());
-
-        return &m_addresses[m_index];
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre this is dereferenceable
-     * \return the generic address
-     */
-    inline Address *operator->() noexcept
-    {
-        assert(m_index <= m_addresses.size());
-
-        return &m_addresses[m_index];
-    }
-
-    /**
-     * Pre-increment the iterator.
-     *
-     * \return this
-     */
-    inline AddressIterator &operator++(int) noexcept
-    {
-        if (m_index + 1 >= m_addresses.size()) {
-            m_addresses.clear();
-            m_index = 0;
-        } else
-            m_index ++;
-
-        return *this;
-    }
-
-    /**
-     * Post-increment the iterator.
-     *
-     * \return copy of this
-     */
-    inline AddressIterator operator++() noexcept
-    {
-        AddressIterator save = *this;
-
-        if (m_index + 1 >= m_addresses.size()) {
-            m_addresses.clear();
-            m_index = 0;
-        } else
-            m_index ++;
-
-        return save;
-    }
-
-    friend bool operator==(const AddressIterator &, const AddressIterator &) noexcept;
-    friend bool operator!=(const AddressIterator &, const AddressIterator &) noexcept;
-};
-
-/**
- * Compare two address iterators.
- *
- * \param i1 the first iterator
- * \param i2 the second iterator
- * \return true if they equal
- */
-inline bool operator==(const AddressIterator &i1, const AddressIterator &i2) noexcept
-{
-    return i1.m_addresses == i2.m_addresses && i1.m_index == i2.m_index;
-}
-
-/**
- * Compare two address iterators.
- *
- * \param i1 the first iterator
- * \param i2 the second iterator
- * \return false if they equal
- */
-inline bool operator!=(const AddressIterator &i1, const AddressIterator &i2) noexcept
-{
-    return !(i1 == i2);
-}
-
-/**
- * Compare two generic addresses.
- *
- * \param a1 the first address
- * \param a2 the second address
- * \return true if they equal
- */
-inline bool operator==(const Address &a1, const Address &a2) noexcept
-{
-    return a1.length() == a2.length() && std::memcmp(a1.get(), a2.get(), a1.length()) == 0;
-}
-
-/**
- * Compare two generic addresses.
- *
- * \param a1 the first address
- * \param a2 the second address
- * \return false if they equal
- */
-inline bool operator!=(const Address &a1, const Address &a2) noexcept
-{
-    return !(a1 == a2);
-}
-
-/**
- * \brief Base socket class.
- */
-class Socket {
-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
-     * \throw Error on errors
-     */
-    Socket(int domain, int type, int protocol)
-    {
-#if !defined(NET_NO_AUTO_INIT)
-        init();
-#endif
-        m_handle = ::socket(domain, type, protocol);
-
-        if (m_handle == Invalid)
-            throw Error();
-    }
-
-    /**
-     * Create the socket with an already defined handle and its protocol.
-     *
-     * \param handle the handle
-     */
-    explicit inline Socket(Handle handle) noexcept
-        : m_handle(handle)
-    {
-    }
-
-    /**
-     * 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_handle(other.m_handle)
-    {
-        other.m_handle = Invalid;
-    }
-
-    /**
-     * Default destructor.
-     */
-    virtual ~Socket()
-    {
-        close();
-    }
-
-    /**
-     * Tells if the socket is not invalid.
-     *
-     * \return true if not invalid
-     */
-    inline bool isOpen() const noexcept
-    {
-        return m_handle != Invalid;
-    }
-
-    /**
-     * Set an option for the socket. Wrapper of setsockopt(2).
-     *
-     * \pre isOpen()
-     * \param level the setting level
-     * \param name the name
-     * \param arg the value
-     * \throw Error on errors
-     */
-    template <typename Argument>
-    inline void set(int level, int name, const Argument &arg)
-    {
-        assert(m_handle != Invalid);
-
-        if (::setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure)
-            throw Error();
-    }
-
-    /**
-     * Object-oriented option setter.
-     *
-     * The object must have `set(Socket &) const`.
-     *
-     * \pre isOpen()
-     * \param option the option
-     * \throw Error on errors
-     */
-    template <typename Option>
-    inline void set(const Option &option)
-    {
-        assert(m_handle != Invalid);
-
-        option.set(*this);
-    }
-
-    /**
-     * Get an option for the socket. Wrapper of getsockopt(2).
-     *
-     * \pre isOpen()
-     * \param level the setting level
-     * \param name the name
-     * \return the value
-     * \throw Error on errors
-     */
-    template <typename Argument>
-    Argument get(int level, int name)
-    {
-        assert(m_handle != Invalid);
-
-        Argument desired, result{};
-        socklen_t size = sizeof (result);
-
-        if (::getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure)
-            throw Error();
-
-        std::memcpy(&result, &desired, size);
-
-        return result;
-    }
-
-    /**
-     * Object-oriented option getter.
-     *
-     * The object must have `T get(Socket &) const`, T can be any type and it is
-     * the value returned from this function.
-     *
-     * \pre isOpen()
-     * \return the same value as get() in the option
-     * \throw Error on errors
-     */
-    template <typename Option>
-    inline auto get() -> decltype(std::declval<Option>().get(*this))
-    {
-        assert(m_handle != Invalid);
-
-        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.
-     *
-     * \pre isOpen()
-     * \param address the address
-     * \param length the size
-     * \throw Error on errors
-     */
-    inline void bind(const sockaddr *address, socklen_t length)
-    {
-        assert(m_handle != Invalid);
-
-        if (::bind(m_handle, address, length) == Failure)
-            throw Error();
-    }
-
-    /**
-     * Overload that takes an address.
-     *
-     * \pre isOpen()
-     * \param address the address
-     * \throw Error on errors
-     */
-    inline void bind(const Address &address)
-    {
-        assert(m_handle != Invalid);
-
-        bind(address.get(), address.length());
-    }
-
-    /**
-     * Listen for pending connection.
-     *
-     * \pre isOpen()
-     * \param max the maximum number
-     * \throw Error on errors
-     */
-    inline void listen(int max = 128)
-    {
-        assert(m_handle != Invalid);
-
-        if (::listen(m_handle, max) == Failure)
-            throw Error();
-    }
-
-    /**
-     * Get the local name. This is a wrapper of getsockname().
-     *
-     * \pre isOpen()
-     * \return the address
-     * \throw Error on failures
-     */
-    Address getsockname() const
-    {
-        assert(m_handle != Invalid);
-
-        sockaddr_storage ss;
-        socklen_t length = sizeof (sockaddr_storage);
-
-        if (::getsockname(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
-            throw Error();
-
-        return Address(reinterpret_cast<sockaddr *>(&ss), length);
-    }
-
-    /**
-     * Get connected address. This is a wrapper for getpeername().
-     *
-     * \pre isOpen()
-     * \return the address
-     * \throw Error on failures
-     */
-    Address getpeername() const
-    {
-        assert(m_handle != Invalid);
-
-        sockaddr_storage ss;
-        socklen_t length = sizeof (sockaddr_storage);
-
-        if (::getpeername(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
-            throw Error();
-
-        return Address(reinterpret_cast<sockaddr *>(&ss), length);
-    }
-
-    /**
-     * 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;
-        }
-    }
-
-    /**
-     * 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;
-        other.m_handle = Invalid;
-
-        return *this;
-    }
-};
-
-/**
- * Compare two sockets.
- *
- * \param s1 the first socket
- * \param s2 the second socket
- * \return true if they equals
- */
-inline bool operator==(const Socket &s1, const Socket &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
- */
-inline bool operator!=(const Socket &s1, const Socket &s2)
-{
-    return s1.handle() != s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * \param s1 the first socket
- * \param s2 the second socket
- * \return true if s1 < s2
- */
-inline bool operator<(const Socket &s1, const Socket &s2)
-{
-    return s1.handle() < s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * \param s1 the first socket
- * \param s2 the second socket
- * \return true if s1 > s2
- */
-inline bool operator>(const Socket &s1, const Socket &s2)
-{
-    return s1.handle() > s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * \param s1 the first socket
- * \param s2 the second socket
- * \return true if s1 <= s2
- */
-inline bool operator<=(const Socket &s1, const Socket &s2)
-{
-    return s1.handle() <= s2.handle();
-}
-
-/**
- * Compare two sockets.
- *
- * \param s1 the first socket
- * \param s2 the second socket
- * \return true if s1 >= s2
- */
-inline bool operator>=(const Socket &s1, const Socket &s2)
-{
-    return s1.handle() >= s2.handle();
-}
-
-/**
- * \brief Clear TCP implementation.
- * \ingroup net-module-tcp
- *
- * This is the basic TCP protocol that implements recv, send, connect and accept
- * as wrappers of the usual C functions.
- */
-class TcpSocket : public Socket {
-public:
-    /**
-     * Inherited constructors.
-     */
-    using Socket::Socket;
-
-    /**
-     * Construct a TCP socket.
-     *
-     * \param domain the domain
-     * \param protocol the protocol
-     * \throw Error on errors
-     */
-    inline TcpSocket(int domain, int protocol)
-        : Socket(domain, SOCK_STREAM, protocol)
-    {
-    }
-
-    /**
-     * Get the type of the socket.
-     *
-     * \return the type
-     */
-    inline int type() const noexcept
-    {
-        return SOCK_STREAM;
-    }
-
-    /**
-     * Initiate connection.
-     *
-     * \param address the address
-     * \param length the address length
-     * \throw WouldBlockError if the socket is marked non-blocking and
-     * connection cannot be established immediately
-     * \throw Error on other errors
-     */
-    void connect(const sockaddr *address, socklen_t length)
-    {
-        if (::connect(this->m_handle, address, length) == Failure) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == WSAEWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error(error);
-#else
-            if (errno == EINPROGRESS)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \param address the address
-     * \throw WouldBlockError if the socket is marked non-blocking and
-     * connection cannot be established immediately
-     * \throw Error on other errors
-     */
-    void connect(const Address &address)
-    {
-        connect(address.get(), address.length());
-    }
-
-    /**
-     * Accept a new client.
-     *
-     * If there are no pending connection, an invalid socket is returned.
-     *
-     * \return the new socket
-     * \throw WouldBlockError if the socket is marked non-blocking and no
-     * connection are available
-     * \throw Error on other errors
-     */
-    TcpSocket accept()
-    {
-        Handle handle = ::accept(this->m_handle, nullptr, 0);
-
-        if (handle == Invalid) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == WSAEWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error(error);
-#else
-            if (errno == EWOULDBLOCK || errno == EAGAIN)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-
-        return TcpSocket(handle);
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \return the number of bytes received
-     */
-    unsigned recv(void *data, unsigned length)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread = ::recv(this->m_handle, (Arg)data, max, 0);
-
-        if (nbread == Failure) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == WSAEWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error(error);
-#else
-            if (errno == EAGAIN || errno == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-
-        return static_cast<unsigned>(nbread);
-    }
-
-    /**
-     * Send some data.
-     *
-     * \param data the data to send
-     * \param length the length
-     * \return the number of bytes sent
-     */
-    unsigned send(const void *data, unsigned length)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent = ::send(this->m_handle, (ConstArg)data, max, 0);
-
-        if (nbsent == Failure) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == WSAEWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error();
-#else
-            if (errno == EAGAIN || errno == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-
-        return static_cast<unsigned>(nbsent);
-    }
-};
-
-/**
- * \brief Clear UDP type.
- *
- * This class is the basic implementation of UDP sockets.
- */
-class UdpSocket : public Socket {
-public:
-    /**
-     * Inherited constructors.
-     */
-    using Socket::Socket;
-
-    /**
-     * Construct a TCP socket.
-     *
-     * \param domain the domain
-     * \param protocol the protocol
-     * \throw Error on errors
-     */
-    inline UdpSocket(int domain, int protocol)
-        : Socket(domain, SOCK_DGRAM, protocol)
-    {
-    }
-
-    /**
-     * Get the type of the socket.
-     *
-     * \return the type
-     */
-    inline int type() const noexcept
-    {
-        return SOCK_DGRAM;
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \param data the data
-     * \param length the length
-     * \param address the source address
-     * \param addrlen the source address in/out length
-     * \return the number of bytes received
-     * \throw WouldBlockError if the socket is marked non-blocking and the
-     * operation would block
-     * \throw Error on other errors
-     */
-    unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread = ::recvfrom(this->m_handle, (Arg)data, max, 0, address, addrlen);
-
-        if (nbread == Failure) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error(error);
-#else
-            if (errno == EAGAIN || errno == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-
-        return static_cast<unsigned>(nbread);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \param data the data
-     * \param length the length
-     * \param source the source information (optional)
-     * \throw WouldBlockError if the socket is marked non-blocking and the
-     * operation would block
-     * \throw Error on other errors
-     */
-    inline unsigned recvfrom(void *data, unsigned length, Address *source = nullptr)
-    {
-        sockaddr_storage st;
-        socklen_t socklen = sizeof (sockaddr_storage);
-
-        auto nr = recvfrom(data, length, reinterpret_cast<sockaddr *>(&st), &socklen);
-
-        if (source)
-            *source = Address(reinterpret_cast<const sockaddr *>(&st), socklen);
-
-        return nr;
-    }
-
-    /**
-     * Send some data.
-     *
-     * \param data the data to send
-     * \param length the data length
-     * \param address the destination address
-     * \param addrlen the destination address length
-     * \return the number of bytes sent
-     * \throw WouldBlockError if the socket is marked non-blocking and the
-     * operation would block
-     * \throw Error on other errors
-     */
-    unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent = ::sendto(this->m_handle, (ConstArg)data, max, 0, address, addrlen);
-
-        if (nbsent == Failure) {
-#if defined(_WIN32)
-            int error = WSAGetLastError();
-
-            if (error == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error(error);
-#else
-            if (errno == EAGAIN || errno == EWOULDBLOCK)
-                throw WouldBlockError();
-            else
-                throw Error();
-#endif
-        }
-
-        return static_cast<unsigned>(nbsent);
-    }
-
-    /**
-     * Overloaded function
-     *
-     * \param data the data to send
-     * \param length the data length
-     * \param address the destination address
-     * \return the number of bytes sent
-     * \throw WouldBlockError if the socket is marked non-blocking and the
-     * operation would block
-     * \throw Error on other errors
-     */
-    inline unsigned sendto(const void *data, unsigned length, const Address &address)
-    {
-        return sendto(data, length, address.get(), address.length());
-    }
-};
-
-#if !defined(NET_NO_SSL)
-
-/**
- * \brief Experimental TLS support.
- * \ingroup net-module-tls
- * \warning This class is highly experimental.
- */
-class TlsSocket : public Socket {
-public:
-    /**
-     * \brief SSL connection mode.
-     */
-    enum Mode {
-        Server,         //!< Use Server when you accept a socket server side,
-        Client          //!< Use Client when you connect to a server.
-    };
-
-private:
-    using Context = std::shared_ptr<SSL_CTX>;
-    using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>;
-
-    // Determine if we created a TlsSocket from a temporary or a lvalue.
-    bool m_mustclose{false};
-
-    Context m_context;
-    Ssl m_ssl{nullptr, nullptr};
-
-    inline std::string error()
-    {
-        BIO *bio = BIO_new(BIO_s_mem());
-        char *buf = nullptr;
-
-        ERR_print_errors(bio);
-
-        std::size_t length = BIO_get_mem_data (bio, &buf);
-        std::string result(buf, length);
-
-        BIO_free(bio);
-
-        return result;
-    }
-
-    template <typename Function>
-    void wrap(Function &&function)
-    {
-        auto ret = function();
-
-        if (ret <= 0) {
-            int no = SSL_get_error(m_ssl.get(), ret);
-
-            switch (no) {
-            case SSL_ERROR_WANT_READ:
-                throw WantReadError();
-            case SSL_ERROR_WANT_WRITE:
-                throw WantWriteError();
-            default:
-                throw Error(error());
-            }
-        }
-    }
-
-    void create(Mode mode, const SSL_METHOD *method)
-    {
-#if !defined(NET_NO_SSL_AUTO_INIT)
-        ssl::init();
-#endif
-        m_context = Context(SSL_CTX_new(method), SSL_CTX_free);
-        m_ssl = Ssl(SSL_new(m_context.get()), SSL_free);
-
-        SSL_set_fd(m_ssl.get(), this->m_handle);
-
-        if (mode == Server)
-            SSL_set_accept_state(m_ssl.get());
-        else
-            SSL_set_connect_state(m_ssl.get());
-    }
-
-public:
-    /**
-     * Create a socket around an existing one.
-     *
-     * The original socket is moved to this instance and must not be used
-     * anymore.
-     *
-     * \param sock the TCP socket
-     * \param mode the mode
-     * \param method the method
-     */
-    TlsSocket(TcpSocket &&sock, Mode mode = Server, const SSL_METHOD *method = TLSv1_method())
-        : Socket(std::move(sock))
-        , m_mustclose(true)
-    {
-        create(mode, method);
-    }
-
-    /**
-     * Wrap a socket around an existing one without taking ownership.
-     *
-     * The original socket must still exist until this TlsSocket is closed.
-     *
-     * \param sock the TCP socket
-     * \param mode the mode
-     * \param method the method
-     */
-    TlsSocket(TcpSocket &sock, Mode mode = Server, const SSL_METHOD *method = TLSv1_method())
-        : Socket(sock.handle())
-    {
-        create(mode, method);
-    }
-
-    /**
-     * Destroy the socket if owned.
-     */
-    ~TlsSocket()
-    {
-        /**
-         * If the socket has been created from a rvalue this class owns the
-         * socket and will close it in the parent destructor.
-         *
-         * Otherwise, when created from a lvalue, mark this socket as invalid
-         * to avoid double close'ing it as two sockets points to the same
-         * descriptor.
-         */
-        if (!m_mustclose)
-            m_handle = Invalid;
-    }
-
-    /**
-     * Get the type of socket.
-     *
-     * \return the type
-     */
-    inline int type() const noexcept
-    {
-        return SOCK_STREAM;
-    }
-
-    /**
-     * Use the specified private key file.
-     *
-     * \param file the path to the private key
-     * \param type the type of file
-     */
-    inline void setPrivateKey(std::string file, int type = SSL_FILETYPE_PEM)
-    {
-        if (SSL_use_PrivateKey_file(m_ssl.get(), file.c_str(), type) != 1)
-            throw Error(error());
-    }
-
-    /**
-     * Use the specified certificate file.
-     *
-     * \param file the path to the file
-     * \param type the type of file
-     */
-    inline void setCertificate(std::string file, int type = SSL_FILETYPE_PEM)
-    {
-        if (SSL_use_certificate_file(m_ssl.get(), file.c_str(), type) != 1)
-            throw Error(error());
-    }
-
-    /**
-     * Do handshake, needed in some case when you have non blocking sockets.
-     */
-    void handshake()
-    {
-        wrap([this] () -> int {
-            return SSL_do_handshake(m_ssl.get());
-        });
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \return the number of bytes received
-     */
-    unsigned recv(void *data, unsigned length)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread = 0;
-
-        wrap([&] () -> int {
-            return nbread = SSL_read(m_ssl.get(), data, max);
-        });
-
-        return static_cast<unsigned>(nbread < 0 ? 0 : nbread);
-    }
-
-    /**
-     * Send some data.
-     *
-     * \param data the data to send
-     * \param length the length
-     * \return the number of bytes sent
-     */
-    unsigned send(const void *data, unsigned length)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent = 0;
-
-        wrap([&] () -> int {
-            return nbsent = SSL_write(m_ssl.get(), data, max);
-        });
-
-        return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent);
-    }
-};
-
-#endif // !NET_NO_SSL
-
-/**
- * \brief IPv4 functions.
- */
-namespace ipv4 {
-
-/**
- * Create an address to bind on any.
- *
- * \param port the port
- * \return the address
- */
-inline Address any(std::uint16_t port)
-{
-    sockaddr_in sin;
-    socklen_t length = sizeof (sockaddr_in);
-
-    std::memset(&sin, 0, sizeof (sockaddr_in));
-    sin.sin_family = AF_INET;
-    sin.sin_addr.s_addr = INADDR_ANY;
-    sin.sin_port = htons(port);
-
-    return Address(reinterpret_cast<const sockaddr *>(&sin), length);
-}
-
-/**
- * Create an address from a IPv4 string.
- *
- * \param ip the ip address
- * \param port the port
- * \return the address
- * \throw Error if inet_pton is unavailable
- */
-inline Address pton(const std::string &ip, std::uint16_t port)
-{
-#if defined(NET_HAVE_INET_PTON)
-#if !defined(NET_NO_AUTO_INIT)
-    init();
-#endif
-
-    sockaddr_in sin;
-    socklen_t length = sizeof (sockaddr_in);
-
-    std::memset(&sin, 0, sizeof (sockaddr_in));
-    sin.sin_family = AF_INET;
-    sin.sin_port = htons(port);
-
-    if (inet_pton(AF_INET, ip.c_str(), &sin.sin_addr) <= 0)
-        throw Error();
-
-    return Address(reinterpret_cast<const sockaddr *>(&sin), length);
-#else
-    (void)ip;
-    (void)port;
-
-    throw Error(std::strerror(ENOSYS));
-#endif
-}
-
-/**
- * Get the underlying ip from the given address.
- *
- * \pre address.domain() == AF_INET
- * \param address the IPv6 address
- * \return the ip address
- * \throw Error if inet_ntop is unavailable
- */
-inline std::string ntop(const Address &address)
-{
-    assert(address.domain() == AF_INET);
-
-#if !defined(NET_NO_AUTO_INIT)
-    init();
-#endif
-
-#if defined(NET_HAVE_INET_NTOP)
-    char result[INET_ADDRSTRLEN + 1];
-
-    std::memset(result, 0, sizeof (result));
-
-    if (!inet_ntop(AF_INET, const_cast<in_addr *>(&address.as<sockaddr_in>().sin_addr), result, sizeof (result)))
-        throw Error();
-
-    return result;
-#else
-    (void)address;
-
-    throw Error(std::strerror(ENOSYS));
-#endif
-}
-
-/**
- * Get the port from the IPv4 address.
- *
- * \pre address.domain() == AF_INET4
- * \param address the address
- * \return the port
- */
-inline std::uint16_t port(const Address &address) noexcept
-{
-    assert(address.domain() == AF_INET);
-
-    return ntohs(address.as<sockaddr_in>().sin_port);
-}
-
-} // !ipv4
-
-/**
- * \brief IPv6 functions.
- */
-namespace ipv6 {
-
-/**
- * Create an address to bind on any.
- *
- * \param port the port
- * \return the address
- */
-inline Address any(std::uint16_t port)
-{
-    sockaddr_in6 sin6;
-    socklen_t length = sizeof (sockaddr_in6);
-
-    std::memset(&sin6, 0, sizeof (sockaddr_in6));
-    sin6.sin6_family = AF_INET6;
-    sin6.sin6_addr = in6addr_any;
-    sin6.sin6_port = htons(port);
-
-    return Address(reinterpret_cast<const sockaddr *>(&sin6), length);
-}
-
-/**
- * Create an address from a IPv4 string.
- *
- * \param ip the ip address
- * \param port the port
- * \return the address
- * \throw Error if inet_pton is unavailable
- */
-inline Address pton(const std::string &ip, std::uint16_t port)
-{
-#if defined(NET_HAVE_INET_PTON)
-#if !defined(NET_NO_AUTO_INIT)
-    init();
-#endif
-
-    sockaddr_in6 sin6;
-    socklen_t length = sizeof (sockaddr_in6);
-
-    std::memset(&sin6, 0, sizeof (sockaddr_in6));
-    sin6.sin6_family = AF_INET6;
-    sin6.sin6_port = htons(port);
-
-    if (inet_pton(AF_INET6, ip.c_str(), &sin6.sin6_addr) <= 0)
-        throw Error();
-
-    return Address(reinterpret_cast<const sockaddr *>(&sin6), length);
-#else
-    (void)ip;
-    (void)port;
-
-    throw Error(std::strerror(ENOSYS));
-#endif
-}
-
-/**
- * Get the underlying ip from the given address.
- *
- * \pre address.domain() == AF_INET6
- * \param address the IPv6 address
- * \return the ip address
- * \throw Error if inet_ntop is unavailable
- */
-inline std::string ntop(const Address &address)
-{
-    assert(address.domain() == AF_INET6);
-
-#if defined(NET_HAVE_INET_NTOP)
-#if !defined(NET_NO_AUTO_INIT)
-    init();
-#endif
-
-    char ret[INET6_ADDRSTRLEN];
-
-    std::memset(ret, 0, sizeof (ret));
-
-    if (!inet_ntop(AF_INET6, const_cast<in6_addr *>(&address.as<sockaddr_in6>().sin6_addr), ret, sizeof (ret)))
-        throw Error();
-
-    return ret;
-#else
-    (void)address;
-
-    throw Error(std::strerror(ENOSYS));
-#endif
-}
-
-/**
- * Get the port from the IPv6 address.
- *
- * \pre address.domain() == AF_INET6
- * \param address the address
- * \return the port
- */
-inline std::uint16_t port(const Address &address) noexcept
-{
-    assert(address.domain() == AF_INET6);
-
-    return ntohs(address.as<sockaddr_in6>().sin6_port);
-}
-
-} // !ipv6
-
-#if !defined(_WIN32)
-
-/**
- * \brief Unix domain functions.
- */
-namespace local {
-
-/**
- * Construct an address to a path.
- *
- * \pre !path.empty()
- * \param path the path
- * \param rm remove the file before (default: false)
- */
-inline Address create(const std::string &path, bool rm = false) noexcept
-{
-    assert(!path.empty());
-
-    // Silently remove the file even if it fails.
-    if (rm)
-        remove(path.c_str());
-
-    sockaddr_un sun;
-    socklen_t length;
-
-    std::memset(sun.sun_path, 0, sizeof (sun.sun_path));
-    std::strncpy(sun.sun_path, path.c_str(), sizeof (sun.sun_path) - 1);
-
-    sun.sun_family = AF_LOCAL;
-
-#if defined(NET_HAVE_SUN_LEN)
-    length = SUN_LEN(&sun);
-#else
-    length = sizeof (sun);
-#endif
-
-    return Address(reinterpret_cast<const sockaddr *>(&sun), length);
-}
-
-/**
- * Get the path from the address.
- *
- * \pre address.domain() == AF_LOCAL
- * \param address the local address
- * \return the path to the socket file
- */
-inline std::string path(const Address &address)
-{
-    assert(address.domain() == AF_LOCAL);
-
-    return reinterpret_cast<const sockaddr_un *>(address.get())->sun_path;
-}
-
-} // !local
-
-#endif // !_WIN32
-
-/**
- * \brief Predefined options.
- */
-namespace option {
-
-/**
- * \ingroup net-module-options
- * \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 {
-private:
-    bool m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * By default the blocking mode is set to true.
-     *
-     * \param value set to true to make blocking sockets
-     */
-    inline SockBlockMode(bool value = true) noexcept
-        : m_value(value)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    void set(Socket &sc) const
-    {
-#if defined(O_NONBLOCK) && !defined(_WIN32)
-        int flags;
-
-        if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0)
-            flags = 0;
-
-        if (m_value)
-            flags &= ~(O_NONBLOCK);
-        else
-            flags |= O_NONBLOCK;
-
-        if (fcntl(sc.handle(), F_SETFL, flags) < 0)
-            throw Error();
-#else
-        unsigned long flags = (m_value) ? 0 : 1;
-
-        if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure)
-            throw Error();
-#endif
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    bool get(Socket &sc) const
-    {
-#if defined(O_NONBLOCK) && !defined(_WIN32)
-        int flags = fcntl(sc.handle(), F_GETFL, 0);
-
-        if (flags < 0)
-            throw Error();
-
-        return !(flags & O_NONBLOCK);
-#else
-        (void)sc;
-
-        throw Error(std::strerror(ENOSYS));
-#endif
-    }
-};
-
-/**
- * \ingroup net-module-options
- * \brief Set or get the input buffer.
- */
-class SockReceiveBuffer {
-private:
-    int m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * \param size the buffer size
-     */
-    inline SockReceiveBuffer(int size = 2048) noexcept
-        : m_value(size)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    inline void set(Socket &sc) const
-    {
-        sc.set(SOL_SOCKET, SO_RCVBUF, m_value);
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    inline int get(Socket &sc) const
-    {
-        return sc.get<int>(SOL_SOCKET, SO_RCVBUF);
-    }
-};
-
-/**
- * \ingroup net-module-options
- * \brief Reuse address, must be used before calling Socket::bind
- */
-class SockReuseAddress {
-private:
-    bool m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * By default the option reuses the address.
-     *
-     * \param value set to true to reuse the address
-     */
-    inline SockReuseAddress(bool value = true) noexcept
-        : m_value(value)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    inline void set(Socket &sc) const
-    {
-        sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0);
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    inline bool get(Socket &sc) const
-    {
-        return sc.get<int>(SOL_SOCKET, SO_REUSEADDR) != 0;
-    }
-};
-
-/**
- * \ingroup net-module-options
- * \brief Set or get the output buffer.
- */
-class SockSendBuffer {
-private:
-    int m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * \param size the buffer size
-     */
-    inline SockSendBuffer(int size = 2048) noexcept
-        : m_value(size)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    inline void set(Socket &sc) const
-    {
-        sc.set(SOL_SOCKET, SO_SNDBUF, m_value);
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    inline int get(Socket &sc) const
-    {
-        return sc.get<int>(SOL_SOCKET, SO_SNDBUF);
-    }
-};
-
-/**
- * \ingroup net-module-options
- * \brief Set this option if you want to disable nagle's algorithm.
- */
-class TcpNoDelay {
-private:
-    bool m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * By default disable TCP delay.
-     *
-     * \param value set to true to disable TCP delay
-     */
-    inline TcpNoDelay(bool value = true) noexcept
-        : m_value(value)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    inline void set(Socket &sc) const
-    {
-        sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0);
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    inline bool get(Socket &sc) const
-    {
-        return sc.get<int>(IPPROTO_TCP, TCP_NODELAY) != 0;
-    }
-};
-
-/**
- * \ingroup net-module-options
- * \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 {
-private:
-    bool m_value;
-
-public:
-    /**
-     * Create the option.
-     *
-     * By default with want IPv6 only.
-     *
-     * \param value set to true to use IPv6 only
-     */
-    inline Ipv6Only(bool value = true) noexcept
-        : m_value(value)
-    {
-    }
-
-    /**
-     * Set the option.
-     *
-     * \param sc the socket
-     * \throw Error on errors
-     */
-    inline void set(Socket &sc) const
-    {
-        sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0);
-    }
-
-    /**
-     * Get the option.
-     *
-     * \param sc the socket
-     * \return the value
-     * \throw Error on errors
-     */
-    inline bool get(Socket &sc) const
-    {
-        return sc.get<int>(IPPROTO_IPV6, IPV6_V6ONLY) != 0;
-    }
-};
-
-} // !option
-
-/**
- * \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::unordered_map<Handle, Condition>;
-
-/**
- * \brief Predefined backends for Listener.
- */
-namespace backend {
-
-#if defined(NET_HAVE_EPOLL)
-
-/**
- * \ingroup net-module-backends
- * \brief Linux's epoll.
- */
-class Epoll {
-private:
-    int m_handle{-1};
-    std::vector<epoll_event> m_events;
-
-    Epoll(const Epoll &) = delete;
-    Epoll &operator=(const Epoll &) = delete;
-
-    std::uint32_t toEpoll(Condition condition) const noexcept
-    {
-        std::uint32_t events = 0;
-
-        if ((condition & Condition::Readable) == Condition::Readable)
-            events |= EPOLLIN;
-        if ((condition & Condition::Writable) == Condition::Writable)
-            events |= EPOLLOUT;
-
-        return events;
-    }
-
-    Condition toCondition(std::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 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();
-    }
-
-public:
-    /**
-     * Create epoll.
-     *
-     * \throw Error on failures
-     */
-    inline Epoll()
-        : m_handle(epoll_create1(0))
-    {
-        if (m_handle < 0)
-            throw Error();
-    }
-
-    /**
-     * Move constructor.
-     *
-     * \param other the other backend
-     */
-    inline Epoll(Epoll &&other) noexcept
-        : m_handle(other.m_handle)
-    {
-        other.m_handle = -1;
-    }
-
-    /**
-     * Close the kqueue descriptor.
-     */
-    inline ~Epoll()
-    {
-        if (m_handle != -1)
-            close(m_handle);
-    }
-
-    /**
-     * Get the backend name.
-     *
-     * \return kqueue
-     */
-    inline std::string name() const noexcept
-    {
-        return "epoll";
-    }
-
-    /**
-     * For set and unset, we need to apply the whole flags required, so if the
-     * socket was set to Connection::Readable and user **adds**
-     * Connection::Writable, we must set both.
-     *
-     * \param table the listener table
-     * \param h the handle
-     * \param condition the condition
-     * \param add set to true if the socket is new to the backend
-     * \throw Error on failures
-     */
-    void set(const ListenerTable &table, Handle h, Condition condition, bool add)
-    {
-        if (add) {
-            update(h, EPOLL_CTL_ADD, toEpoll(condition));
-            m_events.resize(m_events.size() + 1);
-        } else
-            update(h, EPOLL_CTL_MOD, toEpoll(table.at(h) | 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.
-     *
-     * \param table the listener table
-     * \param h the handle
-     * \param condition the condition
-     * \param add set to true if the socket is new to the backend
-     * \throw Error on failures
-     */
-    void unset(const ListenerTable &table, Handle h, Condition condition, bool remove)
-    {
-        if (remove) {
-            update(h, EPOLL_CTL_DEL, 0);
-            m_events.resize(m_events.size() - 1);
-        } else
-            update(h, EPOLL_CTL_MOD, toEpoll(table.at(h) & ~(condition)));
-    }
-
-    /**
-     * Wait for sockets to be ready.
-     *
-     * \param ms the milliseconds timeout
-     * \return the sockets ready
-     * \throw Error on failures
-     */
-    std::vector<ListenerStatus> 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 TimeoutError();
-        if (ret < 0)
-            throw Error();
-
-        for (int i = 0; i < ret; ++i)
-            result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
-
-        return result;
-    }
-
-    /**
-     * Move operator.
-     *
-     * \param other the other
-     * \return this
-     */
-    inline Epoll &operator=(Epoll &&other)
-    {
-        m_handle = other.m_handle;
-        other.m_handle = -1;
-
-        return *this;
-    }
-};
-
-#endif // !NET_HAVE_EPOLL
-
-#if defined(NET_HAVE_KQUEUE)
-
-/**
- * \ingroup net-module-backends
- * \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;
-
-    void 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();
-    }
-
-public:
-    /**
-     * Create kqueue.
-     *
-     * \throw Error on failures
-     */
-    inline Kqueue()
-        : m_handle(kqueue())
-    {
-        if (m_handle < 0)
-            throw Error();
-    }
-
-    /**
-     * Move constructor.
-     *
-     * \param other the other backend
-     */
-    inline Kqueue(Kqueue &&other) noexcept
-        : m_handle(other.m_handle)
-    {
-        other.m_handle = -1;
-    }
-
-    /**
-     * Close the kqueue descriptor.
-     */
-    inline ~Kqueue()
-    {
-        if (m_handle != -1)
-            close(m_handle);
-    }
-
-    /**
-     * Get the backend name.
-     *
-     * \return kqueue
-     */
-    inline std::string name() const noexcept
-    {
-        return "kqueue";
-    }
-
-    /**
-     * Set socket.
-     *
-     * \param h the handle
-     * \param condition the condition
-     * \param add set to true if the socket is new to the backend
-     * \throw Error on failures
-     */
-    void 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);
-    }
-
-    /**
-     * Unset socket.
-     *
-     * \param h the handle
-     * \param condition the condition
-     * \param remove set to true if the socket is completely removed
-     * \throw Error on failures
-     */
-    void 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);
-    }
-
-    /**
-     * Wait for sockets to be ready.
-     *
-     * \param ms the milliseconds timeout
-     * \return the sockets ready
-     * \throw Error on failures
-     */
-    std::vector<ListenerStatus> 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 TimeoutError();
-        if (nevents < 0)
-            throw Error();
-
-        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;
-    }
-
-    /**
-     * Move operator.
-     *
-     * \param other the other
-     * \return this
-     */
-    inline Kqueue &operator=(Kqueue &&other) noexcept
-    {
-        m_handle = other.m_handle;
-        other.m_handle = -1;
-
-        return *this;
-    }
-};
-
-#endif // !NET_HAVE_KQUEUE
-
-#if defined(NET_HAVE_POLL)
-
-/**
- * \ingroup net-module-backends
- * \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 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 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;
-    }
-
-public:
-    /**
-     * Get the backend name.
-     *
-     * \return kqueue
-     */
-    inline std::string name() const noexcept
-    {
-        return "poll";
-    }
-
-    /**
-     * Set socket.
-     *
-     * \param h the handle
-     * \param condition the condition
-     * \param add set to true if the socket is new to the backend
-     * \throw Error on failures
-     */
-    void 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);
-        }
-    }
-
-    /**
-     * Unset socket.
-     *
-     * \param h the handle
-     * \param condition the condition
-     * \param remove set to true if the socket is completely removed
-     * \throw Error on failures
-     */
-    void 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));
-    }
-
-    /**
-     * Wait for sockets to be ready.
-     *
-     * \param ms the milliseconds timeout
-     * \return the sockets ready
-     * \throw Error on failures
-     */
-    std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
-    {
-#if defined(_WIN32)
-        auto result = WSAPoll(m_fds.data(), (ULONG)m_fds.size(), ms);
-#else
-        auto result = poll(m_fds.data(), m_fds.size(), ms);
-#endif
-
-        if (result == 0)
-            throw TimeoutError();
-        if (result < 0)
-            throw Error();
-
-        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 // !NET_HAVE_POLL
-
-/**
- * \ingroup net-module-backends
- * \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:
-    /**
-     * Get the backend name.
-     *
-     * \return select
-     */
-    inline std::string name() const
-    {
-        return "select";
-    }
-
-    /**
-     * No-op.
-     */
-    inline void set(const ListenerTable &, Handle, Condition, bool) noexcept
-    {
-    }
-
-    /**
-     * No-op.
-     */
-    inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept
-    {
-    }
-
-    /**
-     * Wait for sockets to be ready.
-     *
-     * \param table the listener table
-     * \param ms the milliseconds timeout
-     * \return the sockets ready
-     * \throw Error on failures
-     */
-    std::vector<ListenerStatus> 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(static_cast<int>(max + 1), &readset, &writeset, nullptr, towait);
-
-        if (error == Failure)
-            throw Error();
-        if (error == 0)
-            throw TimeoutError();
-
-        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;
-    }
-};
-
-} // !backend
-
-/**
- * \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.
- */
-template <typename Backend = backend :: NET_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.
-     *
-     * \return the number of sockets
-     */
-    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)
-    {
-        auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
-        auto max = cvt.count() > INT_MAX ? INT_MAX : static_cast<int>(cvt.count());
-
-        return m_backend.wait(m_table, max)[0];
-    }
-
-    /**
-     * Overload with milliseconds.
-     *
-     * \param timeout the optional timeout in milliseconds
-     * \return the socket ready
-     */
-    inline ListenerStatus wait(long long 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)
-    {
-        auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
-
-        return m_backend.wait(m_table, cvt.count());
-    }
-
-    /**
-     * Overload with milliseconds.
-     *
-     * \param timeout the optional timeout in milliseconds
-     * \return the socket ready
-     */
-    inline std::vector<ListenerStatus> waitMultiple(int timeout = -1)
-    {
-        return waitMultiple(std::chrono::milliseconds(timeout));
-    }
-};
-
-/**
- * \ingroup net-module-resolv
- *
- * Resolve an hostname immediately.
- *
- * \param host the hostname
- * \param service the service (e.g. http or port number)
- * \param domain the domain (e.g. AF_INET)
- * \param type the type (e.g. SOCK_STREAM)
- * \return the address iterator
- * \throw Error on failures
- */
-inline AddressIterator resolve(const std::string &host,
-                               const std::string &service,
-                               int domain = AF_UNSPEC,
-                               int type = 0)
-{
-#if !defined(NET_NO_AUTO_INIT)
-        init();
-#endif
-
-    struct addrinfo hints, *res, *p;
-
-    std::memset(&hints, 0, sizeof (hints));
-    hints.ai_family = domain;
-    hints.ai_socktype = type;
-
-    int e = getaddrinfo(host.c_str(), service.c_str(), &hints, &res);
-
-    if (e != 0)
-        throw Error(gai_strerror(e));
-
-    std::vector<Address> addresses;
-
-    for (p = res; p != nullptr; p = p->ai_next)
-        addresses.push_back(Address(p->ai_addr, p->ai_addrlen));
-
-    return AddressIterator(addresses, 0);
-}
-
-/**
- * \ingroup net-module-resolv
- *
- * Resolve the first address.
- *
- * \param host the hostname
- * \param service the service name
- * \param domain the domain (e.g. AF_INET)
- * \param type the type (e.g. SOCK_STREAM)
- * \return the first generic address available
- * \throw Error on failures
- * \note do not use AF_UNSPEC and 0 as type for this function
- */
-inline Address resolveOne(const std::string &host, const std::string &service, int domain, int type)
-{
-    AddressIterator it = resolve(host, service, domain, type);
-    AddressIterator end;
-
-    if (it == end)
-        throw Error("no address available");
-
-    return *it;
-}
-
-} // !net
-
-} // !irccd
-
-#endif // !IRCCD_NET_HPP
--- a/lib/irccd/options.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +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.hpp"
-
-namespace irccd {
-
-namespace option {
-
-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;
-}
-
-} // !option
-
-} // !irccd
--- a/lib/irccd/options.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +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 OPTIONS_HPP
-#define OPTIONS_HPP
-
-/**
- * \file options.hpp
- * \brief Basic Unix options parser.
- */
-
-#include <exception>
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * Namespace for options parsing.
- */
-namespace option {
-
-/**
- * \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();
-    }
-};
-
-/**
- * \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 m_message;
-    std::string m_option;
-
-public:
-    /**
-     * Construct the exception.
-     *
-     * \param option the option that requires a value
-     */
-    inline MissingValue(std::string option)
-        : m_option(std::move(option))
-    {
-        m_message = std::string("missing argument for: ") + m_option;
-    }
-
-    /**
-     * Get the options that requires a value.
-     *
-     * \return the option name
-     */
-    inline const std::string &option() const noexcept
-    {
-        return m_option;
-    }
-
-    /**
-     * Get the error message.
-     *
-     * \return the error message
-     */
-    const char *what() const noexcept override
-    {
-        return m_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
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT Result read(int &argc, char **&argv, const Options &definition);
-
-} // !option
-
-} // !irccd
-
-#endif // !OPTIONS_HPP
--- a/lib/irccd/path.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,535 +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 "sysconfig.hpp"
-
-#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.hpp"
-#endif
-
-#include "fs.hpp"
-#include "path.hpp"
-#include "system.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-namespace path {
-
-namespace {
-
-/*
- * Base program directory
- * ------------------------------------------------------------------
- *
- * This variable stores the program base directory.
- *
- * If it is empty, the program was not able to detect it (e.g. error, not
- * supported).
- */
-
-std::string base{"."};
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-
-std::string executablePath()
-{
-    std::string result;
-    std::size_t size = MAX_PATH;
-
-    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
-
-/*
- * System paths
- * ------------------------------------------------------------------
- *
- * Compute system paths.
- *
- * Do not call any of these functions if irccd is relocatable and base is unset.
- */
-
-std::string systemConfig()
-{
-    return base + WITH_CONFDIR;
-}
-
-std::string systemData()
-{
-    return base + WITH_DATADIR;
-}
-
-std::string systemCache()
-{
-    return base + WITH_CACHEDIR;
-}
-
-std::string systemPlugins()
-{
-    return base + WITH_PLUGINDIR;
-}
-
-std::string systemNativePlugins()
-{
-    return base + WITH_NPLUGINDIR;
-}
-
-/*
- * 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)
-{
-    try {
-        base = executablePath();
-    } catch (const std::exception &) {
-        /*
-         * If an exception is thrown, that means the operating 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());
-}
-
-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 <= PathNativePlugins);
-    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;
-        case PathNativePlugins:
-            result = clean(systemNativePlugins());
-            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 PathNativePlugins:
-        case PathPlugins:
-            result = clean(userPlugins());
-            break;
-        default:
-            break;
-        }
-    default:
-        break;
-    }
-
-    return result;
-}
-
-std::vector<std::string> list(Path path)
-{
-    assert(path >= PathConfig && path <= PathNativePlugins);
-
-    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;
-    case PathNativePlugins:
-        list.push_back(clean(fs::cwd()));
-        list.push_back(clean(systemNativePlugins()));
-        break;
-    default:
-        break;
-    }
-
-    return list;
-}
-
-} // !path
-
-} // !irccd
--- a/lib/irccd/path.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * path.hpp -- 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_HPP
-#define IRCCD_PATH_HPP
-
-/**
- * \file path.hpp
- * \brief Path management.
- */
-
-#include <string>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * \brief Namespace for paths.
- */
-namespace path {
-
-/**
- * brief 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
-    PathNativePlugins   //!< Path to native 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])
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT std::string get(Path path, Owner owner);
-
-/**
- * Generic function for multiple paths.
- *
- * This function will add more directories than pathSystem*() and pathUser*()
- * functions.
- *
- * \pre setApplicationPath must have been called
- * \param path the type of path
- * \return the list of preferred directories in order
- */
-IRCCD_EXPORT std::vector<std::string> list(Path path);
-
-} // !path
-
-} // !irccd
-
-#endif // !IRCCD_PATH_HPP
--- a/lib/irccd/plugin-dynlib.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/*
- * plugin-dynlib.cpp -- native plugin implementation
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "fs.hpp"
-#include "logger.hpp"
-#include "path.hpp"
-#include "plugin-dynlib.hpp"
-
-namespace irccd {
-
-namespace {
-
-template <typename Sym>
-inline Sym sym(Dynlib &dynlib, const std::string &name)
-{
-    try {
-        return dynlib.sym<Sym>(name);
-    } catch (...) {
-        return nullptr;
-    }
-}
-
-template <typename Sym, typename... Args>
-inline void call(Sym sym, Args&&... args)
-{
-    if (sym)
-        sym(std::forward<Args>(args)...);
-}
-
-} // !namespace
-
-DynlibPlugin::DynlibPlugin(std::string name, std::string path)
-    : Plugin(name, path)
-    , m_dso(std::move(path))
-    , m_onCommand(sym<OnCommand>(m_dso, "irccd_onCommand"))
-    , m_onConnect(sym<OnConnect>(m_dso, "irccd_onConnect"))
-    , m_onChannelMode(sym<OnChannelMode>(m_dso, "irccd_onChannelMode"))
-    , m_onChannelNotice(sym<OnChannelNotice>(m_dso, "irccd_onChannelNotice"))
-    , m_onInvite(sym<OnInvite>(m_dso, "irccd_onInvite"))
-    , m_onJoin(sym<OnJoin>(m_dso, "irccd_onJoin"))
-    , m_onKick(sym<OnKick>(m_dso, "irccd_onKick"))
-    , m_onLoad(sym<OnLoad>(m_dso, "irccd_onLoad"))
-    , m_onMessage(sym<OnMessage>(m_dso, "irccd_onMessage"))
-    , m_onMe(sym<OnMe>(m_dso, "irccd_onMe"))
-    , m_onMode(sym<OnMode>(m_dso, "irccd_onMode"))
-    , m_onNames(sym<OnNames>(m_dso, "irccd_onNames"))
-    , m_onNick(sym<OnNick>(m_dso, "irccd_onNick"))
-    , m_onNotice(sym<OnNotice>(m_dso, "irccd_onNotice"))
-    , m_onPart(sym<OnPart>(m_dso, "irccd_onPart"))
-    , m_onQuery(sym<OnQuery>(m_dso, "irccd_onQuery"))
-    , m_onQueryCommand(sym<OnQueryCommand>(m_dso, "irccd_onQueryCommand"))
-    , m_onReload(sym<OnReload>(m_dso, "irccd_onReload"))
-    , m_onTopic(sym<OnTopic>(m_dso, "irccd_onTopic"))
-    , m_onUnload(sym<OnUnload>(m_dso, "irccd_onUnload"))
-    , m_onWhois(sym<OnWhois>(m_dso, "irccd_onWhois"))
-{
-}
-
-void DynlibPlugin::onCommand(Irccd &irccd, const MessageEvent &ev)
-{
-    call(m_onCommand, irccd, ev);
-}
-
-void DynlibPlugin::onConnect(Irccd &irccd, const ConnectEvent &ev)
-{
-    call(m_onConnect, irccd, ev);
-}
-
-void DynlibPlugin::onChannelMode(Irccd &irccd, const ChannelModeEvent &ev)
-{
-    call(m_onChannelMode, irccd, ev);
-}
-
-void DynlibPlugin::onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &ev)
-{
-    call(m_onChannelNotice, irccd, ev);
-}
-
-void DynlibPlugin::onInvite(Irccd &irccd, const InviteEvent &ev)
-{
-    call(m_onInvite, irccd, ev);
-}
-
-void DynlibPlugin::onJoin(Irccd &irccd, const JoinEvent &ev)
-{
-    call(m_onJoin, irccd, ev);
-}
-
-void DynlibPlugin::onKick(Irccd &irccd, const KickEvent &ev)
-{
-    call(m_onKick, irccd, ev);
-}
-
-void DynlibPlugin::onLoad(Irccd &irccd)
-{
-    call(m_onLoad, irccd, *this);
-}
-
-void DynlibPlugin::onMessage(Irccd &irccd, const MessageEvent &ev)
-{
-    call(m_onMessage, irccd, ev);
-}
-
-void DynlibPlugin::onMe(Irccd &irccd, const MeEvent &ev)
-{
-    call(m_onMe, irccd, ev);
-}
-
-void DynlibPlugin::onMode(Irccd &irccd, const ModeEvent &ev)
-{
-    call(m_onMode, irccd, ev);
-}
-
-void DynlibPlugin::onNames(Irccd &irccd, const NamesEvent &ev)
-{
-    call(m_onNames, irccd, ev);
-}
-
-void DynlibPlugin::onNick(Irccd &irccd, const NickEvent &ev)
-{
-    call(m_onNick, irccd, ev);
-}
-
-void DynlibPlugin::onNotice(Irccd &irccd, const NoticeEvent &ev)
-{
-    call(m_onNotice, irccd, ev);
-}
-
-void DynlibPlugin::onPart(Irccd &irccd, const PartEvent &ev)
-{
-    call(m_onPart, irccd, ev);
-}
-
-void DynlibPlugin::onQuery(Irccd &irccd, const QueryEvent &ev)
-{
-    call(m_onQuery, irccd, ev);
-}
-
-void DynlibPlugin::onQueryCommand(Irccd &irccd, const QueryEvent &ev)
-{
-    call(m_onQueryCommand, irccd, ev);
-}
-
-void DynlibPlugin::onReload(Irccd &irccd)
-{
-    call(m_onReload, irccd, *this);
-}
-
-void DynlibPlugin::onTopic(Irccd &irccd, const TopicEvent &ev)
-{
-    call(m_onTopic, irccd, ev);
-}
-
-void DynlibPlugin::onUnload(Irccd &irccd)
-{
-    call(m_onUnload, irccd, *this);
-}
-
-void DynlibPlugin::onWhois(Irccd &irccd, const WhoisEvent &ev)
-{
-    call(m_onWhois, irccd, ev);
-}
-
-std::shared_ptr<Plugin> DynlibPluginLoader::open(const std::string &id,
-                                                 const std::string &path) noexcept
-{
-    if (path.rfind(DYNLIB_SUFFIX) == std::string::npos)
-        return nullptr;
-
-    try {
-        return std::make_shared<DynlibPlugin>(id, path);
-    } catch (const std::exception &ex) {
-        log::warning() << "plugin " << id << ": " << ex.what() << std::endl;
-    }
-
-    return nullptr;
-}
-
-std::shared_ptr<Plugin> DynlibPluginLoader::find(const std::string &id) noexcept
-{
-    for (const auto &dir : path::list(path::PathNativePlugins)) {
-        auto path = dir + id + DYNLIB_SUFFIX;
-
-        if (!fs::isReadable(path))
-            continue;
-
-        log::info() << "plugin " << id << ": trying " << path << std::endl;
-
-        return open(id, path);
-    }
-
-    return nullptr;
-}
-
-} // !irccd
--- a/lib/irccd/plugin-dynlib.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +0,0 @@
-/*
- * plugin-dynlib.hpp -- native plugin implementation
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_DYNLIB_HPP
-#define IRCCD_PLUGIN_DYNLIB_HPP
-
-/**
- * \file plugin-dynlib.hpp
- * \brief Native plugin implementation.
- */
-
-#include "dynlib.hpp"
-#include "plugin.hpp"
-
-namespace irccd {
-
-/**
- * \brief Dynlib based plugin.
- * \ingroup plugins
- */
-class DynlibPlugin : public Plugin {
-private:
-    using OnCommand = void (*)(Irccd &, const MessageEvent &);
-    using OnConnect = void (*)(Irccd &, const ConnectEvent &);
-    using OnChannelMode = void (*)(Irccd &, const ChannelModeEvent &);
-    using OnChannelNotice = void (*)(Irccd &, const ChannelNoticeEvent &);
-    using OnInvite = void (*)(Irccd &, const InviteEvent &);
-    using OnJoin = void (*)(Irccd &, const JoinEvent &);
-    using OnKick = void (*)(Irccd &, const KickEvent &);
-    using OnLoad = void (*)(Irccd &, DynlibPlugin &);
-    using OnMessage = void (*)(Irccd &, const MessageEvent &);
-    using OnMe = void (*)(Irccd &, const MeEvent &);
-    using OnMode = void (*)(Irccd &, const ModeEvent &);
-    using OnNames = void (*)(Irccd &, const NamesEvent &);
-    using OnNick = void (*)(Irccd &, const NickEvent &);
-    using OnNotice = void (*)(Irccd &, const NoticeEvent &);
-    using OnPart = void (*)(Irccd &, const PartEvent &);
-    using OnQuery = void (*)(Irccd &, const QueryEvent &);
-    using OnQueryCommand = void (*)(Irccd &, const QueryEvent &);
-    using OnReload = void (*)(Irccd &, DynlibPlugin &);
-    using OnTopic = void (*)(Irccd &, const TopicEvent &);
-    using OnUnload = void (*)(Irccd &, DynlibPlugin &);
-    using OnWhois = void (*)(Irccd &, const WhoisEvent &);
-
-    Dynlib m_dso;
-    OnCommand m_onCommand;
-    OnConnect m_onConnect;
-    OnChannelMode m_onChannelMode;
-    OnChannelNotice m_onChannelNotice;
-    OnInvite m_onInvite;
-    OnJoin m_onJoin;
-    OnKick m_onKick;
-    OnLoad m_onLoad;
-    OnMessage m_onMessage;
-    OnMe m_onMe;
-    OnMode m_onMode;
-    OnNames m_onNames;
-    OnNick m_onNick;
-    OnNotice m_onNotice;
-    OnPart m_onPart;
-    OnQuery m_onQuery;
-    OnQueryCommand m_onQueryCommand;
-    OnReload m_onReload;
-    OnTopic m_onTopic;
-    OnUnload m_onUnload;
-    OnWhois m_onWhois;
-
-    // Configuration and formats.
-    PluginConfig m_config;
-    PluginFormats m_formats;
-
-public:
-    /**
-     * Construct the plugin.
-     *
-     * \param name the name
-     * \param path the fully resolved path (must be absolute)
-     * \throw std::exception on failures
-     */
-    DynlibPlugin(std::string name, std::string path);
-
-    /**
-     * \copydoc Plugin::onCommand
-     */
-    IRCCD_EXPORT void onCommand(Irccd &irccd, const MessageEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onConnect
-     */
-    IRCCD_EXPORT void onConnect(Irccd &irccd, const ConnectEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onChannelMode
-     */
-    IRCCD_EXPORT void onChannelMode(Irccd &irccd, const ChannelModeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onChannelNotice
-     */
-    IRCCD_EXPORT void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onInvite
-     */
-    IRCCD_EXPORT void onInvite(Irccd &irccd, const InviteEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onJoin
-     */
-    IRCCD_EXPORT void onJoin(Irccd &irccd, const JoinEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onKick
-     */
-    IRCCD_EXPORT void onKick(Irccd &irccd, const KickEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onLoad
-     */
-    IRCCD_EXPORT void onLoad(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onMessage
-     */
-    IRCCD_EXPORT void onMessage(Irccd &irccd, const MessageEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onMe
-     */
-    IRCCD_EXPORT void onMe(Irccd &irccd, const MeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onMode
-     */
-    IRCCD_EXPORT void onMode(Irccd &irccd, const ModeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNames
-     */
-    IRCCD_EXPORT void onNames(Irccd &irccd, const NamesEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNick
-     */
-    IRCCD_EXPORT void onNick(Irccd &irccd, const NickEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNotice
-     */
-    IRCCD_EXPORT void onNotice(Irccd &irccd, const NoticeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onPart
-     */
-    IRCCD_EXPORT void onPart(Irccd &irccd, const PartEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onQuery
-     */
-    IRCCD_EXPORT void onQuery(Irccd &irccd, const QueryEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onQueryCommand
-     */
-    IRCCD_EXPORT void onQueryCommand(Irccd &irccd, const QueryEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onReload
-     */
-    IRCCD_EXPORT void onReload(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onTopic
-     */
-    IRCCD_EXPORT void onTopic(Irccd &irccd, const TopicEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onUnload
-     */
-    IRCCD_EXPORT void onUnload(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onWhois
-     */
-    IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override;
-};
-
-/**
- * \brief Implementation for searching native plugins.
- */
-class DynlibPluginLoader : public PluginLoader {
-public:
-    /**
-     * \copydoc PluginLoader::find
-     */
-    std::shared_ptr<Plugin> open(const std::string &id,
-                                 const std::string &path) noexcept override;
-
-    /**
-     * \copydoc PluginLoader::find
-     */
-    std::shared_ptr<Plugin> find(const std::string &id) noexcept override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_PLUGIN_DYNLIB_HPP
--- a/lib/irccd/plugin-js.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,467 +0,0 @@
-/*
- * plugin-js.cpp -- JavaScript plugins 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.
- */
-
-#include "sysconfig.hpp"
-
-#if defined(HAVE_STAT)
-#  include <sys/stat.h>
-#  include <cerrno>
-#  include <cstring>
-#endif
-
-#include "fs.hpp"
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "mod-plugin.hpp"
-#include "mod-server.hpp"
-#include "plugin-js.hpp"
-#include "service-module.hpp"
-#include "service-plugin.hpp"
-#include "timer.hpp"
-
-namespace irccd {
-
-const char JsPlugin::ConfigProperty[] = "\xff""\xff""irccd-plugin-config";
-const char JsPlugin::FormatProperty[] = "\xff""\xff""irccd-plugin-format";
-
-std::unordered_map<std::string, std::string> JsPlugin::getTable(const char *name) const
-{
-    StackAssert sa(m_context);
-    std::unordered_map<std::string, std::string> result;
-
-    duk_get_global_string(m_context, name);
-    dukx_enumerate(m_context, -1, 0, true, [&] (auto ctx) {
-        result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1));
-    });
-    duk_pop(m_context);
-
-    return result;
-}
-
-void JsPlugin::putTable(const char *name, const std::unordered_map<std::string, std::string> &vars)
-{
-    StackAssert sa(m_context);
-
-    duk_get_global_string(m_context, name);
-
-    for (const auto &pair : vars) {
-        dukx_push_std_string(m_context, pair.second);
-        duk_put_prop_string(m_context, -2, pair.first.c_str());
-    }
-
-    duk_pop(m_context);
-}
-
-void JsPlugin::call(const std::string &name, unsigned nargs)
-{
-    duk_get_global_string(m_context, name.c_str());
-
-    if (duk_get_type(m_context, -1) == DUK_TYPE_UNDEFINED)
-        // Function not defined, remove the undefined value and all arguments.
-        duk_pop_n(m_context, nargs + 1);
-    else {
-        // Call the function and discard the result.
-        duk_insert(m_context, -nargs - 1);
-
-        if (duk_pcall(m_context, nargs) != 0)
-            throw dukx_exception(m_context, -1, true);
-
-        duk_pop(m_context);
-    }
-}
-
-void JsPlugin::putModules(Irccd &irccd)
-{
-    m_modules = irccd.modules().list();
-
-    for (const auto &module : irccd.modules().list())
-        module->load(irccd, std::static_pointer_cast<JsPlugin>(shared_from_this()));
-}
-
-void JsPlugin::putVars()
-{
-    StackAssert sa(m_context);
-
-    duk_push_pointer(m_context, this);
-    duk_put_global_string(m_context, "\xff""\xff""plugin");
-    dukx_push_std_string(m_context, name());
-    duk_put_global_string(m_context, "\xff""\xff""name");
-    dukx_push_std_string(m_context, path());
-    duk_put_global_string(m_context, "\xff""\xff""path");
-}
-
-void JsPlugin::putPath(const std::string &varname, const std::string &append, path::Path type)
-{
-    StackAssert sa(m_context);
-
-    bool found = true;
-    std::string foundpath;
-
-    // Use the first existing directory available.
-    for (const auto &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);
-
-    duk_get_global_string(m_context, "Irccd");
-    duk_get_prop_string(m_context, -1, "Plugin");
-    dukx_push_std_string(m_context, foundpath);
-    duk_put_prop_string(m_context, -2, varname.c_str());
-    duk_pop_2(m_context);
-}
-
-JsPlugin::JsPlugin(std::string name, std::string path)
-    : Plugin(name, path)
-{
-    /*
-     * Create two special tables for configuration and formats, they are
-     * referenced later as
-     *
-     *   - Irccd.Plugin.config
-     *   - Irccd.Plugin.format
-     *
-     * In mod-plugin.cpp.
-     */
-    duk_push_object(m_context);
-    duk_put_global_string(m_context, ConfigProperty);
-    duk_push_object(m_context);
-    duk_put_global_string(m_context, FormatProperty);
-}
-
-void JsPlugin::onChannelMode(Irccd &, const ChannelModeEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.mode);
-    dukx_push_std_string(m_context, event.argument);
-    call("onChannelMode", 5);
-}
-
-void JsPlugin::onChannelNotice(Irccd &, const ChannelNoticeEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.message);
-    call("onChannelNotice", 4);
-}
-
-void JsPlugin::onCommand(Irccd &, const MessageEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.message);
-    call("onCommand", 4);
-}
-
-void JsPlugin::onConnect(Irccd &, const ConnectEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    call("onConnect", 1);
-}
-
-void JsPlugin::onInvite(Irccd &, const InviteEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    call("onInvite", 3);
-}
-
-void JsPlugin::onJoin(Irccd &, const JoinEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    call("onJoin", 3);
-}
-
-void JsPlugin::onKick(Irccd &, const KickEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.target);
-    dukx_push_std_string(m_context, event.reason);
-    call("onKick", 5);
-}
-
-void JsPlugin::onLoad(Irccd &irccd)
-{
-    StackAssert sa(m_context);
-
-    /*
-     * 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(path().c_str(), &st) < 0)
-        throw std::runtime_error(std::strerror(errno));
-#endif
-
-    /*
-     * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/)
-     * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/)
-     */
-    putModules(irccd);
-    putVars();
-    putPath("dataPath", "plugin/" + name(), path::PathData);
-    putPath("configPath", "plugin/" + name(), path::PathConfig);
-    putPath("cachePath", "plugin/" + name(), path::PathCache);
-
-    // Try to load the file (does not call onLoad yet).
-    if (duk_peval_file(m_context, path().c_str()) != 0)
-        throw dukx_exception(m_context, -1, true);
-
-    duk_pop(m_context);
-
-    /*
-     * We put configuration and formats after loading the file and before
-     * calling onLoad to allow the plugin adding configuration to
-     * Irccd.Plugin.(config|format) before the user.
-     */
-    setConfig(irccd.plugins().config(name()));
-    setFormats(irccd.plugins().formats(name()));
-
-    // Read metadata .
-    duk_get_global_string(m_context, "info");
-
-    if (duk_get_type(m_context, -1) == DUK_TYPE_OBJECT) {
-        // 'author'
-        duk_get_prop_string(m_context, -1, "author");
-        setAuthor(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : author());
-        duk_pop(m_context);
-
-        // 'license'
-        duk_get_prop_string(m_context, -1, "license");
-        setLicense(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : license());
-        duk_pop(m_context);
-
-        // 'summary'
-        duk_get_prop_string(m_context, -1, "summary");
-        setSummary(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : summary());
-        duk_pop(m_context);
-
-        // 'version'
-        duk_get_prop_string(m_context, -1, "version");
-        setVersion(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : version());
-        duk_pop(m_context);
-    }
-
-    duk_pop(m_context);
-    call("onLoad", 0);
-}
-
-void JsPlugin::onMessage(Irccd &, const MessageEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.message);
-    call("onMessage", 4);
-}
-
-void JsPlugin::onMe(Irccd &, const MeEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.message);
-    call("onMe", 4);
-}
-
-void JsPlugin::onMode(Irccd &, const ModeEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.mode);
-    call("onMode", 3);
-}
-
-void JsPlugin::onNames(Irccd &, const NamesEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_array(m_context, event.names, dukx_push_std_string);
-    call("onNames", 3);
-}
-
-void JsPlugin::onNick(Irccd &, const NickEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.nickname);
-    call("onNick", 3);
-}
-
-void JsPlugin::onNotice(Irccd &, const NoticeEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.message);
-    call("onNotice", 3);
-}
-
-void JsPlugin::onPart(Irccd &, const PartEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.reason);
-    call("onPart", 4);
-}
-
-void JsPlugin::onQuery(Irccd &, const QueryEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.message);
-    call("onQuery", 3);
-}
-
-void JsPlugin::onQueryCommand(Irccd &, const QueryEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.message);
-    call("onQueryCommand", 3);
-}
-
-void JsPlugin::onReload(Irccd &)
-{
-    StackAssert sa(m_context);
-
-    call("onReload");
-}
-
-void JsPlugin::onTopic(Irccd &, const TopicEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    dukx_push_std_string(m_context, event.origin);
-    dukx_push_std_string(m_context, event.channel);
-    dukx_push_std_string(m_context, event.topic);
-    call("onTopic", 4);
-}
-
-void JsPlugin::onUnload(Irccd &irccd)
-{
-    StackAssert sa(m_context);
-
-    call("onUnload");
-
-    for (const auto &module : m_modules)
-        module->unload(irccd, std::static_pointer_cast<JsPlugin>(shared_from_this()));
-}
-
-void JsPlugin::onWhois(Irccd &, const WhoisEvent &event)
-{
-    StackAssert sa(m_context);
-
-    dukx_push_server(m_context, std::move(event.server));
-    duk_push_object(m_context);
-    dukx_push_std_string(m_context, event.whois.nick);
-    duk_put_prop_string(m_context, -2, "nickname");
-    dukx_push_std_string(m_context, event.whois.user);
-    duk_put_prop_string(m_context, -2, "username");
-    dukx_push_std_string(m_context, event.whois.realname);
-    duk_put_prop_string(m_context, -2, "realname");
-    dukx_push_std_string(m_context, event.whois.host);
-    duk_put_prop_string(m_context, -2, "host");
-    dukx_push_array(m_context, event.whois.channels, dukx_push_std_string);
-    duk_put_prop_string(m_context, -2, "channels");
-    call("onWhois", 2);
-}
-
-std::shared_ptr<Plugin> JsPluginLoader::open(const std::string &id,
-                                             const std::string &path) noexcept
-{
-    if (path.rfind(".js") == std::string::npos)
-        return nullptr;
-
-    try {
-        return std::make_shared<JsPlugin>(id, path);
-    } catch (const std::exception &ex) {
-        log::warning() << "plugin " << id << ": " << ex.what() << std::endl;
-    }
-
-    return nullptr;
-}
-
-std::shared_ptr<Plugin> JsPluginLoader::find(const std::string &id) noexcept
-{
-    for (const auto &dir : path::list(path::PathPlugins)) {
-        auto path = dir + id + ".js";
-
-        if (!fs::isReadable(path))
-            continue;
-
-        log::info() << "plugin " << id << ": trying " << path << std::endl;
-
-        return open(id, path);
-    }
-
-    return nullptr;
-}
-
-} // !irccd
--- a/lib/irccd/plugin-js.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-/*
- * plugin-js.hpp -- JavaScript plugins 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.
- */
-
-#ifndef IRCCD_PLUGIN_JS_HPP
-#define IRCCD_PLUGIN_JS_HPP
-
-/**
- * \file plugin-js.hpp
- * \brief JavaScript plugins for irccd.
- */
-
-#include "duktape.hpp"
-#include "path.hpp"
-#include "plugin.hpp"
-
-namespace irccd {
-
-class Module;
-
-/**
- * \brief JavaScript plugins for irccd.
- * \ingroup plugins
- */
-class JsPlugin : public Plugin {
-public:
-    /**
-     * Global property where to read/write plugin configuration (object).
-     */
-    static const char ConfigProperty[];
-
-    /**
-     * Global property where to read/write plugin formats (object).
-     */
-    static const char FormatProperty[];
-
-private:
-    // JavaScript context
-    UniqueContext m_context;
-
-    // Store loaded modules.
-    std::vector<std::shared_ptr<Module>> m_modules;
-
-    // Private helpers.
-    std::unordered_map<std::string, std::string> getTable(const char *name) const;
-    void putTable(const char *name, const std::unordered_map<std::string, std::string> &vars);
-    void call(const std::string &name, unsigned nargs = 0);
-    void putModules(Irccd &irccd);
-    void putVars();
-    void putPath(const std::string &varname, const std::string &append, path::Path type);
-
-public:
-    /**
-     * Constructor.
-     *
-     * \param name the plugin name
-     * \param path the path to the plugin
-     */
-    IRCCD_EXPORT JsPlugin(std::string name, std::string path);
-
-    /**
-     * Access the Duktape context.
-     *
-     * \return the context
-     */
-    inline UniqueContext &context() noexcept
-    {
-        return m_context;
-    }
-
-    /**
-     * \copydoc Plugin::config
-     */
-    PluginConfig config() override
-    {
-        return getTable(ConfigProperty);
-    }
-
-    /**
-     * \copydoc Plugin::setConfig
-     */
-    void setConfig(PluginConfig config) override
-    {
-        putTable(ConfigProperty, config);
-    }
-
-    /**
-     * \copydoc Plugin::formats
-     */
-    PluginFormats formats() override
-    {
-        return getTable(FormatProperty);
-    }
-
-    /**
-     * \copydoc Plugin::setFormats
-     */
-    void setFormats(PluginFormats formats) override
-    {
-        putTable(FormatProperty, formats);
-    }
-
-    /**
-     * \copydoc Plugin::onCommand
-     */
-    IRCCD_EXPORT void onCommand(Irccd &irccd, const MessageEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onConnect
-     */
-    IRCCD_EXPORT void onConnect(Irccd &irccd, const ConnectEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onChannelMode
-     */
-    IRCCD_EXPORT void onChannelMode(Irccd &irccd, const ChannelModeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onChannelNotice
-     */
-    IRCCD_EXPORT void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onInvite
-     */
-    IRCCD_EXPORT void onInvite(Irccd &irccd, const InviteEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onJoin
-     */
-    IRCCD_EXPORT void onJoin(Irccd &irccd, const JoinEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onKick
-     */
-    IRCCD_EXPORT void onKick(Irccd &irccd, const KickEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onLoad
-     */
-    IRCCD_EXPORT void onLoad(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onMessage
-     */
-    IRCCD_EXPORT void onMessage(Irccd &irccd, const MessageEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onMe
-     */
-    IRCCD_EXPORT void onMe(Irccd &irccd, const MeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onMode
-     */
-    IRCCD_EXPORT void onMode(Irccd &irccd, const ModeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNames
-     */
-    IRCCD_EXPORT void onNames(Irccd &irccd, const NamesEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNick
-     */
-    IRCCD_EXPORT void onNick(Irccd &irccd, const NickEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onNotice
-     */
-    IRCCD_EXPORT void onNotice(Irccd &irccd, const NoticeEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onPart
-     */
-    IRCCD_EXPORT void onPart(Irccd &irccd, const PartEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onQuery
-     */
-    IRCCD_EXPORT void onQuery(Irccd &irccd, const QueryEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onQueryCommand
-     */
-    IRCCD_EXPORT void onQueryCommand(Irccd &irccd, const QueryEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onReload
-     */
-    IRCCD_EXPORT void onReload(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onTopic
-     */
-    IRCCD_EXPORT void onTopic(Irccd &irccd, const TopicEvent &event) override;
-
-    /**
-     * \copydoc Plugin::onUnload
-     */
-    IRCCD_EXPORT void onUnload(Irccd &irccd) override;
-
-    /**
-     * \copydoc Plugin::onWhois
-     */
-    IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override;
-};
-
-/**
- * \brief Implementation for searching Javascript plugins.
- */
-class JsPluginLoader : public PluginLoader {
-public:
-    /**
-     * \copydoc PluginLoader::find
-     */
-    std::shared_ptr<Plugin> open(const std::string &id,
-                                 const std::string &path) noexcept override;
-
-    /**
-     * \copydoc PluginLoader::find
-     */
-    std::shared_ptr<Plugin> find(const std::string &id) noexcept override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_PLUGIN_JS_HPP
--- a/lib/irccd/plugin.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,500 +0,0 @@
-/*
- * plugin.hpp -- 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_HPP
-#define IRCCD_PLUGIN_HPP
-
-/**
- * \file plugin.hpp
- * \brief Irccd plugins
- */
-
-/**
- * \defgroup plugins Plugins
- * \brief Plugin management.
- */
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "server.hpp"
-#include "sysconfig.hpp"
-#include "util.hpp"
-
-namespace irccd {
-
-class Irccd;
-
-/**
- * \brief Configuration map extract from config file.
- */
-using PluginConfig = std::unordered_map<std::string, std::string>;
-
-/**
- * \brief Formats for plugins.
- */
-using PluginFormats = std::unordered_map<std::string, std::string>;
-
-/**
- * \ingroup plugins
- * \brief Abstract plugin.
- *
- * A plugin is identified by name and can be loaded and unloaded at runtime.
- */
-class Plugin : public std::enable_shared_from_this<Plugin> {
-private:
-    // Plugin information
-    std::string m_name;
-    std::string m_path;
-
-    // Metadata
-    std::string m_author{"unknown"};
-    std::string m_license{"unknown"};
-    std::string m_summary{"unknown"};
-    std::string m_version{"unknown"};
-
-public:
-    /**
-     * Constructor.
-     *
-     * \param name the plugin id
-     * \param path the fully resolved path to the plugin
-     * \throws std::runtime_error on errors
-     */
-    inline Plugin(std::string name, std::string path) noexcept
-        : m_name(std::move(name))
-        , m_path(std::move(path))
-    {
-    }
-
-    /**
-     * Temporary, close all timers.
-     */
-    virtual ~Plugin() = default;
-
-    /**
-     * Get the plugin name.
-     *
-     * \return the plugin name
-     */
-    inline const std::string &name() const noexcept
-    {
-        return m_name;
-    }
-
-    /**
-     * Get the plugin path.
-     *
-     * \return the plugin path
-     * \note some plugins may not exist on the disk
-     */
-    inline const std::string &path() const noexcept
-    {
-        return m_path;
-    }
-
-    /**
-     * Get the author.
-     *
-     * \return the author
-     */
-    inline const std::string &author() const noexcept
-    {
-        return m_author;
-    }
-
-    /**
-     * Set the author.
-     *
-     * \param author the author
-     */
-    inline void setAuthor(std::string author) noexcept
-    {
-        m_author = std::move(author);
-    }
-
-    /**
-     * Get the license.
-     *
-     * \return the license
-     */
-    inline const std::string &license() const noexcept
-    {
-        return m_license;
-    }
-
-    /**
-     * Set the license.
-     *
-     * \param license the license
-     */
-    inline void setLicense(std::string license) noexcept
-    {
-        m_license = std::move(license);
-    }
-
-    /**
-     * Get the summary.
-     *
-     * \return the summary
-     */
-    inline const std::string &summary() const noexcept
-    {
-        return m_summary;
-    }
-
-    /**
-     * Set the summary.
-     *
-     * \param summary the summary
-     */
-    inline void setSummary(std::string summary) noexcept
-    {
-        m_summary = std::move(summary);
-    }
-
-    /**
-     * Get the version.
-     *
-     * \return the version
-     */
-    inline const std::string &version() const noexcept
-    {
-        return m_version;
-    }
-
-    /**
-     * Set the version.
-     *
-     * \param version the version
-     */
-    inline void setVersion(std::string version) noexcept
-    {
-        m_version = std::move(version);
-    }
-
-    /**
-     * Access the plugin configuration.
-     *
-     * \return the config
-     */
-    virtual PluginConfig config()
-    {
-        return {};
-    }
-
-    /**
-     * Set the configuration.
-     *
-     * \param config the configuration
-     */
-    virtual void setConfig(PluginConfig config)
-    {
-        util::unused(config);
-    }
-
-    /**
-     * Access the plugin formats.
-     *
-     * \return the format
-     */
-    virtual PluginFormats formats()
-    {
-        return {};
-    }
-
-    /**
-     * Set the formats.
-     *
-     * \param formats the formats
-     */
-    virtual void setFormats(PluginFormats formats)
-    {
-        util::unused(formats);
-    }
-
-    /**
-     * On channel message. This event will call onMessage or
-     * onCommand if the messages starts with the command character
-     * plus the plugin name.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onCommand(Irccd &irccd, const MessageEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On successful connection.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onConnect(Irccd &irccd, const ConnectEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On channel mode.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onChannelMode(Irccd &irccd, const ChannelModeEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On a channel notice.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On invitation.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onInvite(Irccd &irccd, const InviteEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On join.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onJoin(Irccd &irccd, const JoinEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On kick.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onKick(Irccd &irccd, const KickEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On load.
-     *
-     * \param irccd the irccd instance
-     */
-    virtual void onLoad(Irccd &irccd)
-    {
-        util::unused(irccd);
-    }
-
-    /**
-     * On channel message.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onMessage(Irccd &irccd, const MessageEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On CTCP Action.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onMe(Irccd &irccd, const MeEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On user mode change.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onMode(Irccd &irccd, const ModeEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On names listing.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onNames(Irccd &irccd, const NamesEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On nick change.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onNick(Irccd &irccd, const NickEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On user notice.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onNotice(Irccd &irccd, const NoticeEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On part.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onPart(Irccd &irccd, const PartEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On user query.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onQuery(Irccd &irccd, const QueryEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On user query command.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onQueryCommand(Irccd &irccd, const QueryEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On reload.
-     *
-     * \param irccd the irccd instance
-     */
-    virtual void onReload(Irccd &irccd)
-    {
-        util::unused(irccd);
-    }
-
-    /**
-     * On topic change.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onTopic(Irccd &irccd, const TopicEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-
-    /**
-     * On unload.
-     *
-     * \param irccd the irccd instance
-     */
-    virtual void onUnload(Irccd &irccd)
-    {
-        util::unused(irccd);
-    }
-
-    /**
-     * On whois information.
-     *
-     * \param irccd the irccd instance
-     * \param event the event
-     */
-    virtual void onWhois(Irccd &irccd, const WhoisEvent &event)
-    {
-        util::unused(irccd, event);
-    }
-};
-
-/**
- * \brief Abstract interface for searching plugins.
- *
- * This class is used to make loading of plugins extensible, the PluginService
- * knows some predefined plugins loaders and use them to search for available
- * plugins.
- *
- * This makes easier to implement new plugins or new ways of loading them.
- *
- * \see DynlibPluginLoader
- * \see JsPluginLoader
- */
-class PluginLoader {
-public:
-    /**
-     * Try to open the plugin specified by path.
-     *
-     * The implementation must test if the plugin is suitable for opening, by
-     * testing extension for example.
-     *
-     * \param file the file
-     */
-    virtual std::shared_ptr<Plugin> open(const std::string &id,
-                                         const std::string &file) noexcept = 0;
-
-    /**
-     * Search for a plugin named by this id.
-     *
-     * \param id the plugin id
-     * \return the plugin
-     */
-    virtual std::shared_ptr<Plugin> find(const std::string &id) noexcept = 0;
-};
-
-} // !irccd
-
-#endif // !IRCCD_PLUGIN_HPP
--- a/lib/irccd/rule.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +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.hpp"
-#include "rule.hpp"
-#include "util.hpp"
-
-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::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/lib/irccd/rule.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-/*
- * rule.hpp -- 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_HPP
-#define IRCCD_RULE_HPP
-
-/**
- * \file rule.hpp
- * \brief Rule description
- */
-
-#include <sstream>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-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 {
-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:
-    /**
-     * 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
-     */
-    IRCCD_EXPORT 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
-     */
-    IRCCD_EXPORT 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
-     */
-    IRCCD_EXPORT RuleAction action() const noexcept;
-
-    /**
-     * Get the servers.
-     *
-     * \return the servers
-     */
-    IRCCD_EXPORT const RuleSet &servers() const noexcept;
-
-    /**
-     * Get the channels.
-     *
-     * \return the channels
-     */
-    IRCCD_EXPORT const RuleSet &channels() const noexcept;
-
-    /**
-     * Get the origins.
-     *
-     * \return the origins
-     */
-    IRCCD_EXPORT const RuleSet &origins() const noexcept;
-
-    /**
-     * Get the plugins.
-     *
-     * \return the plugins
-     */
-    IRCCD_EXPORT const RuleSet &plugins() const noexcept;
-
-    /**
-     * Get the events.
-     *
-     * \return the events
-     */
-    IRCCD_EXPORT const RuleSet &events() const noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_RULE_HPP
--- a/lib/irccd/server.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,907 +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 <format.h>
-
-#include <libircclient.h>
-#include <libirc_rfcnumeric.h>
-
-#include "sysconfig.hpp"
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-#  include <sys/types.h>
-#  include <netinet/in.h>
-#  include <arpa/nameser.h>
-#  include <resolv.h>
-#endif
-
-#include "logger.hpp"
-#include "util.hpp"
-#include "server.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-/*
- * Server::Session declaration.
- * ------------------------------------------------------------------
- */
-
-class Server::Session {
-public:
-    std::unique_ptr<irc_session_t, void (*)(irc_session_t *)> m_handle{nullptr, nullptr};
-
-    inline operator const irc_session_t *() const noexcept
-    {
-        return m_handle.get();
-    }
-
-    inline operator irc_session_t *() noexcept
-    {
-        return m_handle.get();
-    }
-
-    inline bool isConnected() const noexcept
-    {
-        return irc_is_connected(m_handle.get());
-    }
-};
-
-/*
- * Server::State declaration.
- * ------------------------------------------------------------------
- */
-
-class Server::State {
-public:
-    State() = default;
-    virtual ~State() = default;
-    virtual void prepare(Server &, fd_set &, fd_set &, net::Handle &) = 0;
-    virtual std::string ident() const = 0;
-};
-
-/*
- * Server::DisconnectedState declaration.
- * ------------------------------------------------------------------
- */
-
-class Server::DisconnectedState : public Server::State {
-private:
-    ElapsedTimer m_timer;
-
-public:
-    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
-    std::string ident() const override;
-};
-
-/*
- * Server::ConnectingState declaration.
- * ------------------------------------------------------------------
- */
-
-class Server::ConnectingState : public State {
-private:
-    enum {
-        Disconnected,
-        Connecting
-    } m_state{Disconnected};
-
-    ElapsedTimer m_timer;
-
-    bool connect(Server &server);
-
-public:
-    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
-    std::string ident() const override;
-};
-
-/*
- * Server::ConnectedState declaration.
- * ------------------------------------------------------------------
- */
-
-class Server::ConnectedState : public State {
-public:
-    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
-    std::string ident() const override;
-};
-
-namespace {
-
-/*
- * strify
- * ------------------------------------------------------------------
- *
- * Make sure to build a C++ string with a not-null C string.
- */
-inline std::string strify(const char *s)
-{
-    return (s == nullptr) ? "" : std::string(s);
-}
-
-/*
- * cleanPrefix
- * ------------------------------------------------------------------
- *
- * Remove the user prefix only if it is present in the mode table, for example
- * removes @ from @irccd if and only if @ is a character mode (e.g. operator).
- */
-std::string cleanPrefix(const std::map<ChannelMode, char> &modes, std::string nickname)
-{
-    if (nickname.length() == 0)
-        return nickname;
-
-    for (const auto &pair : modes)
-        if (nickname[0] == pair.second)
-            nickname.erase(0, 1);
-
-    return nickname;
-}
-
-/*
- * extractPrefixes
- * ------------------------------------------------------------------
- *
- * Read modes from the IRC event numeric.
- */
-std::map<ChannelMode, char> extractPrefixes(const std::string &line)
-{
-    std::pair<char, char> table[16];
-    std::string buf = line.substr(7);
-    std::map<ChannelMode, char> modes;
-
-    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<ChannelMode>(table[i].first);
-        auto value = table[i].second;
-
-        modes.emplace(key, value);
-    }
-
-    return modes;
-}
-
-} // !namespace
-
-void Server::removeJoinedChannel(const std::string &channel)
-{
-    m_jchannels.erase(std::remove(m_jchannels.begin(), m_jchannels.end(), channel), m_jchannels.end());
-}
-
-void Server::handleConnect(const char *, const char **) noexcept
-{
-    // Reset the number of tried reconnection.
-    m_recocur = 1;
-
-    // Reset the timer.
-    m_timer.reset();
-
-    // Reset joined channels.
-    m_jchannels.clear();
-
-    // Don't forget to change state and notify.
-    next(std::make_unique<ConnectedState>());
-    onConnect(ConnectEvent{shared_from_this()});
-
-    // Auto join listed channels.
-    for (const auto &channel : m_rchannels) {
-        log::info() << "server " << m_name << ": auto joining " << channel.name << std::endl;
-        join(channel.name, channel.password);
-    }
-}
-
-void Server::handleChannel(const char *orig, const char **params) noexcept
-{
-    onMessage(MessageEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
-
-void Server::handleChannelMode(const char *orig, const char **params) noexcept
-{
-    onChannelMode(ChannelModeEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1]), strify(params[2])});
-}
-
-void Server::handleChannelNotice(const char *orig, const char **params) noexcept
-{
-    onChannelNotice(ChannelNoticeEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
-
-void Server::handleCtcpAction(const char *orig, const char **params) noexcept
-{
-    onMe(MeEvent{shared_from_this(), 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_flags & JoinInvite) && isSelf(strify(params[0])))
-        join(strify(params[1]));
-
-    /*
-     * The libircclient says that invite contains the target nickname, it's
-     * quit uncommon to need it so it is passed as the last argument to be
-     * optional in the plugin.
-     */
-    onInvite(InviteEvent{shared_from_this(), strify(orig), strify(params[1]), strify(params[0])});
-}
-
-void Server::handleJoin(const char *orig, const char **params) noexcept
-{
-    if (isSelf(strify(orig)))
-        m_jchannels.push_back(strify(params[0]));
-
-    onJoin(JoinEvent{shared_from_this(), strify(orig), strify(params[0])});
-}
-
-void Server::handleKick(const char *orig, const char **params) noexcept
-{
-    if (isSelf(strify(params[1]))) {
-        // Remove the channel from the joined list.
-        removeJoinedChannel(strify(params[0]));
-
-        // Rejoin the channel if the option has been set and I was kicked.
-        if (m_flags & AutoRejoin)
-            join(strify(params[0]));
-    }
-
-    onKick(KickEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1]), strify(params[2])});
-}
-
-void Server::handleMode(const char *orig, const char **params) noexcept
-{
-    onMode(ModeEvent{shared_from_this(), strify(orig), strify(params[1])});
-}
-
-void Server::handleNick(const char *orig, const char **params) noexcept
-{
-    // Update our nickname.
-    if (isSelf(strify(orig)))
-        m_nickname = strify(params[0]);
-
-    onNick(NickEvent{shared_from_this(), strify(orig), strify(params[0])});
-}
-
-void Server::handleNotice(const char *orig, const char **params) noexcept
-{
-    /*
-     * Like handleInvite, the notice provides the target nickname, we discard
-     * it.
-     */
-    onNotice(NoticeEvent{shared_from_this(), 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(m_modes, 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(NamesEvent{shared_from_this(), params[1], std::vector<std::string>(it->second.begin(), it->second.end())});
-
-            // 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;
-
-        Whois 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(m_modes, s);
-
-            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(WhoisEvent{shared_from_this(), 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) {
-                m_modes = extractPrefixes(params[i]);
-                break;
-            }
-        }
-    }
-}
-
-void Server::handlePart(const char *orig, const char **params) noexcept
-{
-    // Remove the channel from the joined list if I left a channel.
-    if (isSelf(strify(orig)))
-        removeJoinedChannel(strify(params[0]));
-
-    onPart(PartEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
-
-void Server::handlePing(const char *, const char **) noexcept
-{
-    // Reset the timer to detect disconnection.
-    m_timer.reset();
-}
-
-void Server::handleQuery(const char *orig, const char **params) noexcept
-{
-    onQuery(QueryEvent{shared_from_this(), strify(orig), strify(params[1])});
-}
-
-void Server::handleTopic(const char *orig, const char **params) noexcept
-{
-    onTopic(TopicEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
-
-std::shared_ptr<Server> Server::fromJson(const nlohmann::json &object)
-{
-    auto server = std::make_shared<Server>(util::json::requireIdentifier(object, "name"));
-
-    server->setHost(util::json::requireString(object, "host"));
-    server->setPassword(util::json::getString(object, "password"));
-    server->setNickname(util::json::getString(object, "nickname", server->nickname()));
-    server->setRealname(util::json::getString(object, "realname", server->realname()));
-    server->setUsername(util::json::getString(object, "username", server->username()));
-    server->setCtcpVersion(util::json::getString(object, "ctcpVersion", server->ctcpVersion()));
-    server->setCommandCharacter(util::json::getString(object, "commandChar", server->commandCharacter()));
-
-    if (object.find("port") != object.end())
-        server->setPort(util::json::getUintRange<std::uint16_t>(object, "port"));
-    if (util::json::getBool(object, "ipv6"))
-        server->setFlags(server->flags() | Server::Ipv6);
-    if (util::json::getBool(object, "ssl"))
-        server->setFlags(server->flags() | Server::Ssl);
-    if (util::json::getBool(object, "sslVerify"))
-        server->setFlags(server->flags() | Server::SslVerify);
-    if (util::json::getBool(object, "autoRejoin"))
-        server->setFlags(server->flags() | Server::AutoRejoin);
-    if (util::json::getBool(object, "joinInvite"))
-        server->setFlags(server->flags() | Server::JoinInvite);
-
-    return server;
-}
-
-Channel Server::splitChannel(const std::string &value)
-{
-    auto pos = value.find(':');
-
-    if (pos != std::string::npos)
-        return { value.substr(0, pos), value.substr(pos + 1) };
-
-    return { value, "" };
-}
-
-Server::Server(std::string name)
-    : m_name(std::move(name))
-    , m_session(std::make_unique<Session>())
-    , m_state(std::make_unique<ConnectingState>())
-{
-    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_ping = [] (irc_session_t *session, const char *, const char *orig, const char **params, unsigned) {
-        static_cast<Server *>(irc_get_ctx(session))->handlePing(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->m_handle = {irc_create_session(&callbacks), irc_destroy_session};
-
-    // Save this to the session.
-    irc_set_ctx(*m_session, this);
-    irc_set_ctcp_version(*m_session, m_ctcpversion.c_str());
-}
-
-Server::~Server()
-{
-    irc_disconnect(*m_session);
-}
-
-void Server::setNickname(std::string nickname)
-{
-    if (m_session->isConnected())
-        m_queue.push([=] () {
-            return irc_cmd_nick(*m_session, nickname.c_str()) == 0;
-        });
-    else
-        m_nickname = std::move(nickname);
-}
-
-void Server::setCtcpVersion(std::string ctcpversion)
-{
-    m_ctcpversion = std::move(ctcpversion);
-    irc_set_ctcp_version(*m_session, ctcpversion.c_str());
-}
-
-void Server::next(std::unique_ptr<State> state) noexcept
-{
-    m_stateNext = std::move(state);
-}
-
-void Server::update() noexcept
-{
-    if (m_stateNext) {
-        log::debug("server {}: switch state {} -> {}"_format(m_name, m_state->ident(), m_stateNext->ident()));
-
-        m_state = std::move(m_stateNext);
-        m_stateNext = nullptr;
-
-        // Reset channels.
-        m_jchannels.clear();
-    }
-}
-
-void Server::disconnect() noexcept
-{
-    using namespace std::placeholders;
-
-    irc_disconnect(*m_session);
-    onDie();
-}
-
-void Server::reconnect() noexcept
-{
-    irc_disconnect(*m_session);
-    next(std::make_unique<ConnectingState>());
-}
-
-void Server::prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept
-{
-    m_state->prepare(*this, setinput, setoutput, maxfd);
-}
-
-void Server::sync(fd_set &setinput, fd_set &setoutput)
-{
-    /*
-     * 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, &setinput, &setoutput);
-}
-
-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_nickname == target;
-}
-
-void Server::cmode(std::string channel, std::string mode)
-{
-    m_queue.push([=] () {
-        return irc_cmd_channel_mode(*m_session, channel.c_str(), mode.c_str()) == 0;
-    });
-}
-
-void Server::cnotice(std::string channel, std::string message)
-{
-    m_queue.push([=] () {
-        return irc_cmd_notice(*m_session, channel.c_str(), message.c_str()) == 0;
-    });
-}
-
-void Server::invite(std::string target, std::string channel)
-{
-    m_queue.push([=] () {
-        return irc_cmd_invite(*m_session, target.c_str(), channel.c_str()) == 0;
-    });
-}
-
-void Server::join(std::string channel, std::string password)
-{
-    // 1. Add the channel or update it to the requested channels.
-    auto it = std::find_if(m_rchannels.begin(), m_rchannels.end(), [&] (const auto &c) {
-        return c.name == channel;
-    });
-
-    if (it == m_rchannels.end())
-        m_rchannels.push_back({ channel, password });
-    else
-        *it = { channel, password };
-
-    // 2. Join if not found and connected.
-    if (m_session->isConnected())
-        irc_cmd_join(*m_session, channel.c_str(), password.empty() ? nullptr : password.c_str());
-}
-
-void Server::kick(std::string target, std::string channel, std::string reason)
-{
-    m_queue.push([=] () {
-        return irc_cmd_kick(*m_session, target.c_str(), channel.c_str(), reason.c_str()) == 0;
-    });
-}
-
-void Server::me(std::string target, std::string message)
-{
-    m_queue.push([=] () {
-        return irc_cmd_me(*m_session, target.c_str(), message.c_str()) == 0;
-    });
-}
-
-void Server::message(std::string target, std::string message)
-{
-    m_queue.push([=] () {
-        return irc_cmd_msg(*m_session, target.c_str(), message.c_str()) == 0;
-    });
-}
-
-void Server::mode(std::string mode)
-{
-    m_queue.push([=] () {
-        return irc_cmd_user_mode(*m_session, mode.c_str()) == 0;
-    });
-}
-
-void Server::names(std::string channel)
-{
-    m_queue.push([=] () {
-        return irc_cmd_names(*m_session, channel.c_str()) == 0;
-    });
-}
-
-void Server::notice(std::string target, std::string message)
-{
-    m_queue.push([=] () {
-        return irc_cmd_notice(*m_session, target.c_str(), message.c_str()) == 0;
-    });
-}
-
-void Server::part(std::string channel, std::string reason)
-{
-    m_queue.push([=] () -> bool {
-        if (reason.empty())
-            return irc_cmd_part(*m_session, channel.c_str()) == 0;
-
-        return irc_send_raw(*m_session, "PART %s :%s", channel.c_str(), reason.c_str());
-    });
-}
-
-void Server::send(std::string raw)
-{
-    m_queue.push([=] () {
-        return irc_send_raw(*m_session, raw.c_str()) == 0;
-    });
-}
-
-void Server::topic(std::string channel, std::string topic)
-{
-    m_queue.push([=] () {
-        return irc_cmd_topic(*m_session, channel.c_str(), topic.c_str()) == 0;
-    });
-}
-
-void Server::whois(std::string target)
-{
-    m_queue.push([=] () {
-        return irc_cmd_whois(*m_session, target.c_str()) == 0;
-    });
-}
-
-/*
- * Server::DisconnectedState implementation
- * ------------------------------------------------------------------
- */
-
-void Server::DisconnectedState::prepare(Server &server, fd_set &, fd_set &, net::Handle &)
-{
-    if (server.m_recotries == 0) {
-        log::warning() << "server " << server.m_name << ": reconnection disabled, skipping" << std::endl;
-        server.onDie();
-    } else if (server.m_recotries > 0 && server.m_recocur > server.m_recotries) {
-        log::warning() << "server " << server.m_name << ": giving up" << std::endl;
-        server.onDie();
-    } else {
-        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
-            irc_disconnect(*server.m_session);
-
-            server.m_recocur ++;
-            server.next(std::make_unique<ConnectingState>());
-        }
-    }
-}
-
-std::string Server::DisconnectedState::ident() const
-{
-    return "Disconnected";
-}
-
-/*
- * Server::ConnectingState implementation
- * ------------------------------------------------------------------
- */
-
-bool Server::ConnectingState::connect(Server &server)
-{
-    const char *password = server.m_password.empty() ? nullptr : server.m_password.c_str();
-    std::string host = server.m_host;
-    int code;
-
-    // libircclient requires # for SSL connection.
-#if defined(WITH_SSL)
-    if (server.m_flags & Server::Ssl)
-        host.insert(0, 1, '#');
-    if (!(server.m_flags & Server::SslVerify))
-        irc_option_set(*server.m_session, LIBIRC_OPTION_SSL_NO_VERIFY);
-#endif
-
-    if (server.flags() & Server::Ipv6) {
-        code = irc_connect6(*server.m_session, host.c_str(), server.m_port, password,
-                            server.m_nickname.c_str(),
-                            server.m_username.c_str(),
-                            server.m_realname.c_str());
-    } else {
-        code = irc_connect(*server.m_session, host.c_str(), server.m_port, password,
-                           server.m_nickname.c_str(),
-                           server.m_username.c_str(),
-                           server.m_realname.c_str());
-    }
-
-    return code == 0;
-}
-
-void Server::ConnectingState::prepare(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.
-     */
-    if (m_state == Connecting) {
-        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
-            log::warning() << "server " << server.name() << ": timeout while connecting" << std::endl;
-            server.next(std::make_unique<DisconnectedState>());
-        } else if (!irc_is_connected(*server.m_session)) {
-            log::warning() << "server " << server.m_name << ": error while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
-
-            if (server.m_recotries != 0)
-                log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
-
-            server.next(std::make_unique<DisconnectedState>());
-        } else
-            irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-    } else {
-        /*
-         * This is needed if irccd is started before DHCP or if DNS cache is
-         * outdated.
-         */
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-        (void)res_init();
-#endif
-        log::info("server {}: trying to connect to {}, port {}"_format(server.m_name, server.m_host, server.m_port));
-
-        if (!connect(server)) {
-            log::warning() << "server " << server.m_name << ": disconnected while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
-            server.next(std::make_unique<DisconnectedState>());
-        } else {
-            m_state = Connecting;
-
-            if (irc_is_connected(*server.m_session))
-                irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-        }
-    }
-}
-
-std::string Server::ConnectingState::ident() const
-{
-    return "Connecting";
-}
-
-/*
- * Server::ConnectedState implementation
- * ------------------------------------------------------------------
- */
-
-void Server::ConnectedState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-    if (!irc_is_connected(*server.m_session)) {
-        log::warning() << "server " << server.m_name << ": disconnected" << std::endl;
-
-        if (server.m_recodelay > 0)
-            log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
-
-        server.next(std::make_unique<DisconnectedState>());
-    } else if (server.m_timer.elapsed() >= server.m_timeout * 1000) {
-        log::warning() << "server " << server.m_name << ": ping timeout after "
-                   << (server.m_timer.elapsed() / 1000) << " seconds" << std::endl;
-        server.next(std::make_unique<DisconnectedState>());
-    } else
-        irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-}
-
-std::string Server::ConnectedState::ident() const
-{
-    return "Connected";
-}
-
-} // !irccd
--- a/lib/irccd/server.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,957 +0,0 @@
-/*
- * server.hpp -- 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_HPP
-#define IRCCD_SERVER_HPP
-
-/**
- * \file server.hpp
- * \brief IRC Server.
- */
-
-#include <cstdint>
-#include <functional>
-#include <map>
-#include <memory>
-#include <queue>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <json.hpp>
-
-#include "elapsed-timer.hpp"
-#include "net.hpp"
-#include "signals.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class Server;
-
-/**
- * \brief Prefixes for nicknames.
- */
-enum class ChannelMode {
-    Creator         = 'O',                  //!< Channel creator
-    HalfOperator    = 'h',                  //!< Half operator
-    Operator        = 'o',                  //!< Channel operator
-    Protection      = 'a',                  //!< Unkillable
-    Voiced          = 'v'                   //!< Voice power
-};
-
-/**
- * \brief A channel to join with an optional password.
- */
-class Channel {
-public:
-    std::string name;                       //!< the channel to join
-    std::string password;                   //!< the optional password
-};
-
-/**
- * \brief Describe a whois information.
- */
-class Whois {
-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
-};
-
-/**
- * \brief Channel event.
- */
-class ChannelModeEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string mode;                       //!< The mode.
-    std::string argument;                   //!< The mode argument (Optional).
-};
-
-/**
- * \brief Channel notice event.
- */
-class ChannelNoticeEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string message;                    //!< The notice message.
-};
-
-/**
- * \brief Connection success event.
- */
-class ConnectEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-};
-
-/**
- * \brief Invite event.
- */
-class InviteEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string nickname;                   //!< The nickname (you).
-};
-
-/**
- * \brief Join event.
- */
-class JoinEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-};
-
-/**
- * \brief Kick event.
- */
-class KickEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string target;                     //!< The target.
-    std::string reason;                     //!< The reason (Optional).
-};
-
-/**
- * \brief Message event.
- */
-class MessageEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string message;                    //!< The message.
-};
-
-/**
- * \brief CTCP action event.
- */
-class MeEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string message;                    //!< The message.
-};
-
-/**
- * \brief Mode event.
- */
-class ModeEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string mode;                       //!< The mode.
-};
-
-/**
- * \brief Names listing event.
- */
-class NamesEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string channel;                    //!< The channel.
-    std::vector<std::string> names;         //!< The names.
-};
-
-/**
- * \brief Nick change event.
- */
-class NickEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string nickname;                   //!< The new nickname.
-};
-
-/**
- * \brief Notice event.
- */
-class NoticeEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string message;                    //!< The message.
-};
-
-/**
- * \brief Part event.
- */
-class PartEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string reason;                     //!< The reason.
-};
-
-/**
- * \brief Query event.
- */
-class QueryEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string message;                    //!< The message.
-};
-
-/**
- * \brief Topic event.
- */
-class TopicEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    std::string origin;                     //!< The originator.
-    std::string channel;                    //!< The channel.
-    std::string topic;                      //!< The topic message.
-};
-
-/**
- * \brief Whois event.
- */
-class WhoisEvent {
-public:
-    std::shared_ptr<Server> server;         //!< The server.
-    Whois whois;                            //!< The whois information.
-};
-
-/**
- * \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 ServerService in irccd, 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 std::enable_shared_from_this<Server> {
-public:
-    /**
-     * Bridge for libircclient.
-     */
-    class Session;
-
-    /**
-     * \brief Various options for server.
-     */
-    enum {
-        Ipv6        = (1 << 0),             //!< Connect using IPv6
-        Ssl         = (1 << 1),             //!< Use SSL
-        SslVerify   = (1 << 2),             //!< Verify SSL
-        AutoRejoin  = (1 << 3),             //!< Auto rejoin a kick
-        JoinInvite  = (1 << 4)              //!< Join a channel on invitation
-    };
-
-    /**
-     * Signal: onChannelMode
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone changed the channel mode.
-     */
-    Signal<ChannelModeEvent> onChannelMode;
-
-    /**
-     * Signal: onChannelNotice
-     * ----------------------------------------------------------
-     *
-     * Triggered when a notice has been sent on a channel.
-     */
-    Signal<ChannelNoticeEvent> onChannelNotice;
-
-    /**
-     * Signal: onConnect
-     * ----------------------------------------------------------
-     *
-     * Triggered when the server is successfully connected.
-     */
-    Signal<ConnectEvent> onConnect;
-
-    /**
-     * Signal: onDie
-     * ----------------------------------------------------------
-     *
-     * The server is dead.
-     */
-    Signal<> onDie;
-
-    /**
-     * Signal: onInvite
-     * ----------------------------------------------------------
-     *
-     * Triggered when an invite has been sent to you (the bot).
-     */
-    Signal<InviteEvent> onInvite;
-
-    /**
-     * Signal: onJoin
-     * ----------------------------------------------------------
-     *
-     * Triggered when a user has joined the channel, it also includes you.
-     */
-    Signal<JoinEvent> onJoin;
-
-    /**
-     * Signal: onKick
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone has been kicked from a channel.
-     */
-    Signal<KickEvent> onKick;
-
-    /**
-     * ServerEvent: onMessage
-     * ----------------------------------------------------------
-     *
-     * Triggered when a message on a channel has been sent.
-     */
-    Signal<MessageEvent> 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.
-     */
-    Signal<MeEvent> onMe;
-
-    /**
-     * Signal: onMode
-     * ----------------------------------------------------------
-     *
-     * Triggered when the server changed your user mode.
-     */
-    Signal<ModeEvent> onMode;
-
-    /**
-     * Signal: onNames
-     * ----------------------------------------------------------
-     *
-     * Triggered when names listing has finished on a channel.
-     */
-    Signal<NamesEvent> onNames;
-
-    /**
-     * Signal: onNick
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone changed its nickname, it also includes you.
-     */
-    Signal<NickEvent> onNick;
-
-    /**
-     * Signal: onNotice
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone has sent a notice to you.
-     */
-    Signal<NoticeEvent> onNotice;
-
-    /**
-     * Signal: onPart
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone has left the channel.
-     */
-    Signal<PartEvent> onPart;
-
-    /**
-     * Signal: onQuery
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone has sent you a private message.
-     */
-    Signal<QueryEvent> onQuery;
-
-    /**
-     * Signal: onTopic
-     * ----------------------------------------------------------
-     *
-     * Triggered when someone changed the channel topic.
-     */
-    Signal<TopicEvent> onTopic;
-
-    /**
-     * Signal: onWhois
-     * ----------------------------------------------------------
-     *
-     * Triggered when whois information has been received.
-     */
-    Signal<WhoisEvent> onWhois;
-
-private:
-    class State;
-    class ConnectedState;
-    class ConnectingState;
-    class DisconnectedState;
-
-    // Requested and joined channels.
-    std::vector<Channel> m_rchannels;
-    std::vector<std::string> m_jchannels;
-
-    // Identifier.
-    std::string m_name;
-
-    // Connection information
-    std::string m_host;
-    std::string m_password;
-    std::uint16_t m_port{6667};
-    std::uint8_t m_flags{0};
-
-    // Identity.
-    std::string m_nickname{"irccd"};
-    std::string m_username{"irccd"};
-    std::string m_realname{"IRC Client Daemon"};
-    std::string m_ctcpversion{"IRC Client Daemon"};
-
-    // Settings.
-    std::string m_commandCharacter{"!"};
-    std::int8_t m_recotries{-1};
-    std::uint16_t m_recodelay{30};
-    std::uint16_t m_timeout{300};
-
-    // Queue of requests to send.
-    std::queue<std::function<bool ()>> m_queue;
-
-    // libircclient session (bridge).
-    std::unique_ptr<Session> m_session;
-
-    // States.
-    std::unique_ptr<State> m_state;
-    std::unique_ptr<State> m_stateNext;
-
-    // Misc.
-    ElapsedTimer m_timer;
-    std::map<ChannelMode, char> m_modes;
-    std::int8_t m_recocur{0};
-    std::map<std::string, std::set<std::string>> m_namesMap;
-    std::map<std::string, Whois> m_whoisMap;
-
-    // Private helpers.
-    void removeJoinedChannel(const std::string &channel);
-
-    // Handle libircclient callbacks.
-    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 handlePing(const char *, const char **) noexcept;
-    void handleQuery(const char *, const char **) noexcept;
-    void handleTopic(const char *, const char **) noexcept;
-
-public:
-    /**
-     * Convert a JSON object as a server.
-     *
-     * Used in JavaScript API and transport commands.
-     *
-     * \param object the object
-     * \return the server
-     * \throw std::exception on failures
-     */
-    IRCCD_EXPORT static std::shared_ptr<Server> fromJson(const nlohmann::json &object);
-
-    /**
-     * Split a channel from the form channel:password into a ServerChannel
-     * object.
-     *
-     * \param value the value
-     * \return a channel
-     */
-    IRCCD_EXPORT static Channel splitChannel(const std::string &value);
-
-    /**
-     * Construct a server.
-     *
-     * \param name the identifier
-     */
-    IRCCD_EXPORT Server(std::string name);
-
-    /**
-     * Destructor. Close the connection if needed.
-     */
-    IRCCD_EXPORT virtual ~Server();
-
-    /**
-     * Get the server identifier.
-     *
-     * \return the id
-     */
-    inline const std::string &name() const noexcept
-    {
-        return m_name;
-    }
-
-    /**
-     * Get the hostname.
-     *
-     * \return the hostname
-     */
-    inline const std::string &host() const noexcept
-    {
-        return m_host;
-    }
-
-    /**
-     * Set the hostname.
-     *
-     * \param host the hostname
-     */
-    inline void setHost(std::string host) noexcept
-    {
-        m_host = std::move(host);
-    }
-
-    /**
-     * Get the password.
-     *
-     * \return the password
-     */
-    inline const std::string &password() const noexcept
-    {
-        return m_password;
-    }
-
-    /**
-     * Set the password.
-     *
-     * An empty password means no password.
-     *
-     * \param password the password
-     */
-    inline void setPassword(std::string password) noexcept
-    {
-        m_password = std::move(password);
-    }
-
-    /**
-     * Get the port.
-     *
-     * \return the port
-     */
-    inline std::uint16_t port() const noexcept
-    {
-        return m_port;
-    }
-
-    /**
-     * Set the port.
-     *
-     * \param port the port
-     */
-    inline void setPort(std::uint16_t port) noexcept
-    {
-        m_port = port;
-    }
-
-    /**
-     * Get the flags.
-     *
-     * \return the flags
-     */
-    inline std::uint8_t flags() const noexcept
-    {
-        return m_flags;
-    }
-
-    /**
-     * Set the flags.
-     *
-     * \pre flags must be valid
-     * \param flags the flags
-     */
-    inline void setFlags(std::uint8_t flags) noexcept
-    {
-        assert(flags <= 0x1f);
-
-        m_flags = flags;
-    }
-
-    /**
-     * Get the nickname.
-     *
-     * \return the nickname
-     */
-    inline const std::string &nickname() const noexcept
-    {
-        return m_nickname;
-    }
-
-    /**
-     * Set the nickname.
-     *
-     * If the server is connected, send a nickname command to the IRC server,
-     * otherwise change it locally.
-     *
-     * \param nickname the nickname
-     */
-    IRCCD_EXPORT void setNickname(std::string nickname);
-
-    /**
-     * Get the username.
-     *
-     * \return the username
-     */
-    inline const std::string &username() const noexcept
-    {
-        return m_username;
-    }
-
-    /**
-     * Set the username.
-     *
-     * \param name the username
-     * \note the username will be changed on the next connection
-     */
-    inline void setUsername(std::string name) noexcept
-    {
-        m_username = std::move(name);
-    }
-
-    /**
-     * Get the realname.
-     *
-     * \return the realname
-     */
-    inline const std::string &realname() const noexcept
-    {
-        return m_realname;
-    }
-
-    /**
-     * Set the realname.
-     *
-     * \param realname the username
-     * \note the username will be changed on the next connection
-     */
-    inline void setRealname(std::string realname) noexcept
-    {
-        m_realname = std::move(realname);
-    }
-
-    /**
-     * Get the CTCP version.
-     *
-     * \return the CTCP version
-     */
-    inline const std::string &ctcpVersion() const noexcept
-    {
-        return m_ctcpversion;
-    }
-
-    /**
-     * Set the CTCP version.
-     *
-     * \param ctcpversion the version
-     */
-    IRCCD_EXPORT void setCtcpVersion(std::string ctcpversion);
-
-    /**
-     * Get the command character.
-     *
-     * \return the character
-     */
-    inline const std::string &commandCharacter() const noexcept
-    {
-        return m_commandCharacter;
-    }
-
-    /**
-     * Set the command character.
-     *
-     * \pre !commandCharacter.empty()
-     * \param commandCharacter the command character
-     */
-    inline void setCommandCharacter(std::string commandCharacter) noexcept
-    {
-        assert(!commandCharacter.empty());
-
-        m_commandCharacter = std::move(commandCharacter);
-    }
-
-    /**
-     * Get the number of reconnections before giving up.
-     *
-     * \return the number of reconnections
-     */
-    inline std::int8_t reconnectTries() const noexcept
-    {
-        return m_recotries;
-    }
-
-    /**
-     * Set the number of reconnections to test before giving up.
-     *
-     * A value less than 0 means infinite.
-     *
-     * \param reconnectTries the number of reconnections
-     */
-    inline void setReconnectTries(std::int8_t reconnectTries) noexcept
-    {
-        m_recotries = reconnectTries;
-    }
-
-    /**
-     * Get the reconnection delay before retrying.
-     *
-     * \return the number of seconds
-     */
-    inline std::uint16_t reconnectDelay() const noexcept
-    {
-        return m_recodelay;
-    }
-
-    /**
-     * Set the number of seconds before retrying.
-     *
-     * \param reconnectDelay the number of seconds
-     */
-    inline void setReconnectDelay(std::uint16_t reconnectDelay) noexcept
-    {
-        m_recodelay = reconnectDelay;
-    }
-
-    /**
-     * Get the ping timeout.
-     *
-     * \return the ping timeout
-     */
-    inline std::uint16_t pingTimeout() const noexcept
-    {
-        return m_timeout;
-    }
-
-    /**
-     * Set the ping timeout before considering a server as dead.
-     *
-     * \param pingTimeout the delay in seconds
-     */
-    inline void setPingTimeout(std::uint16_t pingTimeout) noexcept
-    {
-        m_timeout = pingTimeout;
-    }
-
-    /**
-     * Get the list of channels joined.
-     *
-     * \return the channels
-     */
-    inline const std::vector<std::string> &channels() const noexcept
-    {
-        return m_jchannels;
-    }
-
-    /**
-     * Set the next state, it is not changed immediately but on next iteration.
-     *
-     * \param state the new state
-     */
-    IRCCD_EXPORT void next(std::unique_ptr<State> state) noexcept;
-
-    /**
-     * Switch to next state if it has.
-     */
-    IRCCD_EXPORT void update() noexcept;
-
-    /**
-     * Force disconnection.
-     */
-    IRCCD_EXPORT void disconnect() noexcept;
-
-    /**
-     * Asks for a reconnection.
-     */
-    IRCCD_EXPORT void reconnect() noexcept;
-
-    /**
-     * Prepare the IRC session.
-     *
-     * \warning Not thread-safe
-     */
-    IRCCD_EXPORT void prepare(fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) noexcept;
-
-    /**
-     * Process incoming/outgoing data after selection.
-     *
-     * \param setinput
-     * \param setoutput
-     * \throw any exception that have been throw from user functions
-     */
-    IRCCD_EXPORT void sync(fd_set &setinput, fd_set &setoutput);
-
-    /**
-     * Determine if the nickname is the bot itself.
-     *
-     * \param nick the nickname to check
-     * \return true if it is the bot
-     */
-    IRCCD_EXPORT bool isSelf(const std::string &nick) const noexcept;
-
-    /**
-     * Change the channel mode.
-     *
-     * \param channel the channel
-     * \param mode the new mode
-     */
-    IRCCD_EXPORT virtual void cmode(std::string channel, std::string mode);
-
-    /**
-     * Send a channel notice.
-     *
-     * \param channel the channel
-     * \param message message notice
-     */
-    IRCCD_EXPORT virtual void cnotice(std::string channel, std::string message);
-
-    /**
-     * Invite a user to a channel.
-     *
-     * \param target the target nickname
-     * \param channel the channel
-     */
-    IRCCD_EXPORT virtual void invite(std::string target, std::string channel);
-
-    /**
-     * Join a channel, the password is optional and can be kept empty.
-     *
-     * \param channel the channel to join
-     * \param password the optional password
-     */
-    IRCCD_EXPORT virtual void join(std::string channel, std::string password = "");
-
-    /**
-     * 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
-     */
-    IRCCD_EXPORT virtual void kick(std::string target, std::string channel, std::string reason = "");
-
-    /**
-     * 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
-     */
-    IRCCD_EXPORT virtual void me(std::string target, std::string message);
-
-    /**
-     * Send a message to the specified target or channel.
-     *
-     * \param target the target
-     * \param message the message
-     */
-    IRCCD_EXPORT virtual void message(std::string target, std::string message);
-
-    /**
-     * Change your user mode.
-     *
-     * \param mode the mode
-     */
-    IRCCD_EXPORT virtual void mode(std::string mode);
-
-    /**
-     * Request the list of names.
-     *
-     * \param channel the channel
-     */
-    IRCCD_EXPORT virtual void names(std::string channel);
-
-    /**
-     * Send a private notice.
-     *
-     * \param target the target
-     * \param message the notice message
-     */
-    IRCCD_EXPORT virtual void notice(std::string target, std::string message);
-
-    /**
-     * 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
-     */
-    IRCCD_EXPORT virtual void part(std::string channel, std::string reason = "");
-
-    /**
-     * 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`)
-     */
-    IRCCD_EXPORT virtual void send(std::string raw);
-
-    /**
-     * Change the channel topic.
-     *
-     * \param channel the channel
-     * \param topic the desired topic
-     */
-    IRCCD_EXPORT virtual void topic(std::string channel, std::string topic);
-
-    /**
-     * Request for whois information.
-     *
-     * \param target the target nickname
-     */
-    IRCCD_EXPORT virtual void whois(std::string target);
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_HPP
--- a/lib/irccd/service-command.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * service-command.cpp -- store remote commands
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "cmd-help.hpp"
-#include "cmd-plugin-config.hpp"
-#include "cmd-plugin-info.hpp"
-#include "cmd-plugin-list.hpp"
-#include "cmd-plugin-load.hpp"
-#include "cmd-plugin-reload.hpp"
-#include "cmd-plugin-unload.hpp"
-#include "cmd-server-cmode.hpp"
-#include "cmd-server-cnotice.hpp"
-#include "cmd-server-connect.hpp"
-#include "cmd-server-disconnect.hpp"
-#include "cmd-server-info.hpp"
-#include "cmd-server-invite.hpp"
-#include "cmd-server-join.hpp"
-#include "cmd-server-kick.hpp"
-#include "cmd-server-list.hpp"
-#include "cmd-server-me.hpp"
-#include "cmd-server-message.hpp"
-#include "cmd-server-mode.hpp"
-#include "cmd-server-nick.hpp"
-#include "cmd-server-notice.hpp"
-#include "cmd-server-part.hpp"
-#include "cmd-server-reconnect.hpp"
-#include "cmd-server-topic.hpp"
-#include "cmd-watch.hpp"
-#include "service-command.hpp"
-
-namespace irccd {
-
-CommandService::CommandService()
-    : m_commands{
-        std::make_shared<command::HelpCommand>(),
-        std::make_shared<command::PluginConfigCommand>(),
-        std::make_shared<command::PluginInfoCommand>(),
-        std::make_shared<command::PluginListCommand>(),
-        std::make_shared<command::PluginLoadCommand>(),
-        std::make_shared<command::PluginReloadCommand>(),
-        std::make_shared<command::PluginUnloadCommand>(),
-        std::make_shared<command::ServerChannelModeCommand>(),
-        std::make_shared<command::ServerChannelNoticeCommand>(),
-        std::make_shared<command::ServerConnectCommand>(),
-        std::make_shared<command::ServerDisconnectCommand>(),
-        std::make_shared<command::ServerInfoCommand>(),
-        std::make_shared<command::ServerInviteCommand>(),
-        std::make_shared<command::ServerJoinCommand>(),
-        std::make_shared<command::ServerKickCommand>(),
-        std::make_shared<command::ServerListCommand>(),
-        std::make_shared<command::ServerMeCommand>(),
-        std::make_shared<command::ServerMessageCommand>(),
-        std::make_shared<command::ServerModeCommand>(),
-        std::make_shared<command::ServerNickCommand>(),
-        std::make_shared<command::ServerNoticeCommand>(),
-        std::make_shared<command::ServerPartCommand>(),
-        std::make_shared<command::ServerReconnectCommand>(),
-        std::make_shared<command::ServerTopicCommand>(),
-        std::make_shared<command::WatchCommand>(),
-    }
-{
-}
-
-bool CommandService::contains(const std::string &name) const noexcept
-{
-    return find(name) != nullptr;
-}
-
-std::shared_ptr<Command> CommandService::find(const std::string &name) const noexcept
-{
-    auto it = std::find_if(m_commands.begin(), m_commands.end(), [&] (const auto &cmd) {
-        return cmd->name() == name;
-    });
-
-    return it == m_commands.end() ? nullptr : *it;
-}
-
-} // !irccd
--- a/lib/irccd/service-command.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * service-command.hpp -- store remote commands
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_COMMAND_HPP
-#define IRCCD_SERVICE_COMMAND_HPP
-
-/**
- * \file service-command.hpp
- * \brief Store remote commands.
- */
-
-#include <memory>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class Command;
-
-/**
- * \brief Store remote commands.
- * \ingroup services
- */
-class CommandService {
-private:
-    std::vector<std::shared_ptr<Command>> m_commands;
-
-public:
-    /**
-     * Default constructor.
-     *
-     * Populate the commands with predefined ones.
-     */
-    IRCCD_EXPORT CommandService();
-
-    inline const std::vector<std::shared_ptr<Command>> &commands() const noexcept
-    {
-        return m_commands;
-    }
-
-    /**
-     * Tells if a command exists.
-     *
-     * \param name the command name
-     * \return true if the command exists
-     */
-    IRCCD_EXPORT bool contains(const std::string &name) const noexcept;
-
-    /**
-     * Find a command by name.
-     *
-     * \param name the command name
-     * \return the command or empty one if not found
-     */
-    IRCCD_EXPORT std::shared_ptr<Command> find(const std::string &name) const noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_COMMAND_HPP
--- a/lib/irccd/service-interrupt.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- * service-interrupt.cpp -- interrupt irccd event loop
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <array>
-
-#include "logger.hpp"
-#include "service-interrupt.hpp"
-
-namespace irccd {
-
-InterruptService::InterruptService()
-    : m_in(AF_INET, 0)
-    , m_out(AF_INET, 0)
-{
-    // Bind a socket to any port.
-    m_in.set(net::option::SockReuseAddress(true));
-    m_in.bind(net::ipv4::any(0));
-    m_in.listen(1);
-
-    // Do the socket pair.
-    m_out.connect(net::ipv4::pton("127.0.0.1", net::ipv4::port(m_in.getsockname())));
-    m_in = m_in.accept();
-    m_out.set(net::option::SockBlockMode(false));
-}
-
-void InterruptService::prepare(fd_set &in, fd_set &, net::Handle &max)
-{
-    FD_SET(m_in.handle(), &in);
-
-    if (m_in.handle() > max)
-        max = m_in.handle();
-}
-
-void InterruptService::sync(fd_set &in, fd_set &)
-{
-    if (FD_ISSET(m_in.handle(), &in)) {
-        static std::array<char, 32> tmp;
-
-        try {
-            log::debug("irccd: interrupt service recv");
-            m_in.recv(tmp.data(), 32);
-        } catch (const std::exception &ex) {
-            log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl;
-        }
-    }
-}
-
-void InterruptService::interrupt() noexcept
-{
-    try {
-        static char byte;
-
-        log::debug("irccd: interrupt service send");
-        m_out.send(&byte, 1);
-    } catch (const std::exception &ex) {
-        log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl;
-    }
-}
-
-} // !irccd
--- a/lib/irccd/service-interrupt.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * service-interrupt.hpp -- interrupt irccd event loop
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_INTERRUPT_HPP
-#define IRCCD_SERVICE_INTERRUPT_HPP
-
-/**
- * \file service-interrupt.hpp
- * \brief Interrupt irccd event loop.
- */
-
-#include "net.hpp"
-
-namespace irccd {
-
-/**
- * \brief Interrupt irccd event loop.
- * \ingroup services
- */
-class InterruptService {
-private:
-    net::TcpSocket m_in;
-    net::TcpSocket m_out;
-
-public:
-    /**
-     * Prepare the socket pair.
-     *
-     * \throw std::runtime_error on errors
-     */
-    IRCCD_EXPORT InterruptService();
-
-    /**
-     * \copydoc Service::prepare
-     */
-    IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * \copydoc Service::sync
-     */
-    IRCCD_EXPORT void sync(fd_set &in, fd_set &out);
-
-    /**
-     * Request interruption.
-     */
-    IRCCD_EXPORT void interrupt() noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_INTERRUPT_HPP
--- a/lib/irccd/service-module.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * service-module.cpp -- store and manage JavaScript modules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "mod-elapsed-timer.hpp"
-#include "mod-directory.hpp"
-#include "mod-file.hpp"
-#include "mod-irccd.hpp"
-#include "mod-logger.hpp"
-#include "mod-plugin.hpp"
-#include "mod-server.hpp"
-#include "mod-system.hpp"
-#include "mod-timer.hpp"
-#include "mod-unicode.hpp"
-#include "mod-util.hpp"
-#include "service-module.hpp"
-
-namespace irccd {
-
-namespace {
-
-auto find(const std::vector<std::shared_ptr<Module>> &modules, const std::string &name) noexcept
-{
-    return std::find_if(modules.begin(), modules.end(), [&] (const auto &module) {
-        return module->name() == name;
-    });
-}
-
-} // !namespace
-
-ModuleService::ModuleService()
-{
-    // Load Irccd global first.
-    m_modules.push_back(std::make_shared<IrccdModule>());
-
-    // Additional modules.
-    m_modules.push_back(std::make_shared<ElapsedTimerModule>());
-    m_modules.push_back(std::make_shared<DirectoryModule>());
-    m_modules.push_back(std::make_shared<FileModule>());
-    m_modules.push_back(std::make_shared<LoggerModule>());
-    m_modules.push_back(std::make_shared<PluginModule>());
-    m_modules.push_back(std::make_shared<ServerModule>());
-    m_modules.push_back(std::make_shared<SystemModule>());
-    m_modules.push_back(std::make_shared<TimerModule>());
-    m_modules.push_back(std::make_shared<UnicodeModule>());
-    m_modules.push_back(std::make_shared<UtilModule>());
-}
-
-std::shared_ptr<Module> ModuleService::get(const std::string &name) const noexcept
-{
-    auto it = find(m_modules, name);
-
-    if (it == m_modules.end())
-        return nullptr;
-
-    return *it;
-}
-
-bool ModuleService::contains(const std::string &name) const
-{
-    return find(m_modules, name) != m_modules.end();
-}
-
-void ModuleService::add(std::shared_ptr<Module> module)
-{
-    assert(!contains(module->name()));
-
-    m_modules.push_back(std::move(module));
-}
-
-} // !irccd
--- a/lib/irccd/service-module.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * service-module.hpp -- store and manage JavaScript modules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_MODULE_HPP
-#define IRCCD_SERVICE_MODULE_HPP
-
-/**
- * \file service-module.hpp
- * \brief Store and manage JavaScript modules.
- */
-
-#include <memory>
-#include <vector>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class Module;
-
-/**
- * \brief Store and manage JavaScript modules.
- * \ingroup services
- */
-class ModuleService {
-private:
-    std::vector<std::shared_ptr<Module>> m_modules;
-
-public:
-    /**
-     * Construct the service and predefined irccd API.
-     */
-    IRCCD_EXPORT ModuleService();
-
-    /**
-     * Get all modules.
-     *
-     * \return the modules
-     */
-    inline const std::vector<std::shared_ptr<Module>> &list() const noexcept
-    {
-        return m_modules;
-    }
-
-    /**
-     * Get a module.
-     *
-     * \param name the module name
-     * \return the module or empty if not found
-     */
-    IRCCD_EXPORT std::shared_ptr<Module> get(const std::string &name) const noexcept;
-
-    /**
-     * Tells if a module exist.
-     *
-     * \param name the name
-     */
-    IRCCD_EXPORT bool contains(const std::string &name) const;
-
-    /**
-     * Add a JavaScript API module.
-     *
-     * \pre module != nullptr
-     * \pre !contains(module)
-     * \param module the module
-     */
-    IRCCD_EXPORT void add(std::shared_ptr<Module> module);
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_MODULE_HPP
--- a/lib/irccd/service-plugin.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * service-plugin.cpp -- manage plugins
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <functional>
-#include <regex>
-#include <stdexcept>
-
-#include <format.h>
-
-#include "fs.hpp"
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "plugin-dynlib.hpp"
-#include "plugin-js.hpp"
-#include "service-plugin.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-PluginService::PluginService(Irccd &irccd) noexcept
-    : m_irccd(irccd)
-{
-    m_loaders.push_back(std::make_unique<DynlibPluginLoader>());
-    m_loaders.push_back(std::make_unique<JsPluginLoader>());
-}
-
-PluginService::~PluginService()
-{
-    for (const auto &plugin : m_plugins)
-        plugin->onUnload(m_irccd);
-}
-
-bool PluginService::has(const std::string &name) const noexcept
-{
-    return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) {
-        return plugin->name() == name;
-    }) > 0;
-}
-
-std::shared_ptr<Plugin> PluginService::get(const std::string &name) const noexcept
-{
-    auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
-        return plugin->name() == name;
-    });
-
-    if (it == m_plugins.end())
-        return nullptr;
-
-    return *it;
-}
-
-std::shared_ptr<Plugin> PluginService::require(const std::string &name) const
-{
-    auto plugin = get(name);
-
-    if (!plugin)
-        throw std::invalid_argument("plugin {} not found"_format(name));
-
-    return plugin;
-}
-
-void PluginService::add(std::shared_ptr<Plugin> plugin)
-{
-    m_plugins.push_back(std::move(plugin));
-}
-
-void PluginService::setConfig(const std::string &name, PluginConfig config)
-{
-    m_config.emplace(name, std::move(config));
-}
-
-PluginConfig PluginService::config(const std::string &name) const
-{
-    auto it = m_config.find(name);
-
-    if (it != m_config.end())
-        return it->second;
-
-    return PluginConfig();
-}
-
-void PluginService::setFormats(const std::string &name, PluginFormats formats)
-{
-    m_formats.emplace(name, std::move(formats));
-}
-
-PluginFormats PluginService::formats(const std::string &name) const
-{
-    auto it = m_formats.find(name);
-
-    if (it != m_formats.end())
-        return it->second;
-
-    return PluginFormats();
-}
-
-std::shared_ptr<Plugin> PluginService::open(const std::string &id,
-                                            const std::string &path)
-{
-    for (const auto &loader : m_loaders) {
-        auto plugin = loader->open(id, path);
-
-        if (plugin)
-            return plugin;
-    }
-
-    return nullptr;
-}
-
-std::shared_ptr<Plugin> PluginService::find(const std::string &id)
-{
-    for (const auto &loader : m_loaders) {
-        auto plugin = loader->find(id);
-
-        if (plugin)
-            return plugin;
-    }
-
-    return nullptr;
-}
-
-void PluginService::load(std::string name, std::string path)
-{
-    if (has(name))
-        return;
-
-    try {
-        std::shared_ptr<Plugin> plugin;
-
-        if (path.empty())
-            plugin = find(name);
-        else
-            plugin = open(name, std::move(path));
-
-        if (plugin) {
-            plugin->setConfig(m_config[name]);
-            plugin->setFormats(m_formats[name]);
-            plugin->onLoad(m_irccd);
-
-            add(std::move(plugin));
-        }
-    } catch (const std::exception &ex) {
-        log::warning("plugin {}: {}"_format(name, ex.what()));
-    }
-}
-
-void PluginService::reload(const std::string &name)
-{
-    auto plugin = get(name);
-
-    if (plugin)
-        plugin->onReload(m_irccd);
-}
-
-void PluginService::unload(const std::string &name)
-{
-    auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
-        return plugin->name() == name;
-    });
-
-    if (it != m_plugins.end()) {
-        (*it)->onUnload(m_irccd);
-        m_plugins.erase(it);
-    }
-}
-
-} // !irccd
--- a/lib/irccd/service-plugin.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * service-plugin.hpp -- manage plugins
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_PLUGIN_HPP
-#define IRCCD_SERVICE_PLUGIN_HPP
-
-/**
- * \file service-plugin.hpp
- * \brief Manage plugins.
- */
-
-#include <unordered_map>
-#include <memory>
-#include <vector>
-
-#include "plugin-js.hpp"
-
-namespace irccd {
-
-class Irccd;
-
-/**
- * \brief Manage plugins.
- * \ingroup services
- */
-class PluginService {
-private:
-    Irccd &m_irccd;
-    std::vector<std::shared_ptr<Plugin>> m_plugins;
-    std::vector<std::unique_ptr<PluginLoader>> m_loaders;
-    std::unordered_map<std::string, PluginConfig> m_config;
-    std::unordered_map<std::string, PluginFormats> m_formats;
-
-public:
-    /**
-     * Create the plugin service.
-     *
-     * \param irccd the irccd instance
-     */
-    IRCCD_EXPORT PluginService(Irccd &irccd) noexcept;
-
-    /**
-     * Destroy plugins.
-     */
-    IRCCD_EXPORT ~PluginService();
-
-    /**
-     * Get the list of plugins.
-     *
-     * \return the list of plugins
-     */
-    inline const std::vector<std::shared_ptr<Plugin>> &list() const noexcept
-    {
-        return m_plugins;
-    }
-
-    /**
-     * Check if a plugin is loaded.
-     *
-     * \param name the plugin id
-     * \return true if has plugin
-     */
-    IRCCD_EXPORT bool has(const std::string &name) const noexcept;
-
-    /**
-     * Get a loaded plugin or null if not found.
-     *
-     * \param name the plugin id
-     * \return the plugin or empty one if not found
-     */
-    IRCCD_EXPORT std::shared_ptr<Plugin> get(const std::string &name) const noexcept;
-
-    /**
-     * Find a loaded plugin.
-     *
-     * \param name the plugin id
-     * \return the plugin
-     * \throws std::out_of_range if not found
-     */
-    IRCCD_EXPORT std::shared_ptr<Plugin> require(const std::string &name) const;
-
-    /**
-     * Add the specified plugin to the registry.
-     *
-     * \pre plugin != nullptr
-     * \param plugin the plugin
-     * \note the plugin is only added to the list, no action is performed on it
-     */
-    IRCCD_EXPORT void add(std::shared_ptr<Plugin> plugin);
-
-    /**
-     * Configure a plugin.
-     *
-     * If the plugin is already loaded, its configuration is updated.
-     *
-     * \param name the plugin name
-     * \param config the new configuration
-     */
-    IRCCD_EXPORT void setConfig(const std::string &name, PluginConfig config);
-
-    /**
-     * Get a configuration for a plugin.
-     *
-     * \param name the plugin name
-     * \return the configuration or default one if not found
-     */
-    IRCCD_EXPORT PluginConfig config(const std::string &name) const;
-
-    /**
-     * Add formatting for a plugin.
-     *
-     * \param name the plugin name
-     * \param formats the formats
-     */
-    IRCCD_EXPORT void setFormats(const std::string &name, PluginFormats formats);
-
-    /**
-     * Get formats for a plugin.
-     *
-     * \param name the plugin name
-     * \return the formats
-     */
-    IRCCD_EXPORT PluginFormats formats(const std::string &name) const;
-
-    /**
-     * Generic function for opening the plugin at the given path.
-     *
-     * This function will search for every PluginLoader and call open() on it,
-     * the first one that success will be returned.
-     *
-     * \param id the plugin id
-     * \param path the path to the file
-     * \return the plugin or nullptr on failures
-     */
-    IRCCD_EXPORT std::shared_ptr<Plugin> open(const std::string &id,
-                                              const std::string &path);
-
-    /**
-     * Generic function for finding a plugin.
-     *
-     * \param id the plugin id
-     * \return the plugin or nullptr on failures
-     */
-    IRCCD_EXPORT std::shared_ptr<Plugin> find(const std::string &id);
-
-    /**
-     * Convenient wrapper that loads a plugin, call onLoad and add it to the
-     * registry.
-     *
-     * Any errors are printed using logger.
-     *
-     * \param name the name
-     * \param path the optional path (searched if empty)
-     */
-    IRCCD_EXPORT void load(std::string name, std::string path = "");
-
-    /**
-     * Unload a plugin and remove it.
-     *
-     * \param name the plugin id
-     */
-    IRCCD_EXPORT void unload(const std::string &name);
-
-    /**
-     * Reload a plugin by calling onReload.
-     *
-     * \param name the plugin name
-     * \throw std::exception on failures
-     */
-    IRCCD_EXPORT void reload(const std::string &name);
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_PLUGIN_HPP
--- a/lib/irccd/service-rule.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * service-rule.cpp -- store and solve rules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <format.h>
-
-#include "logger.hpp"
-#include "service-rule.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-void RuleService::add(Rule rule)
-{
-    m_rules.push_back(std::move(rule));
-}
-
-void RuleService::insert(Rule rule, unsigned position)
-{
-    assert(position <= m_rules.size());
-
-    m_rules.insert(m_rules.begin() + position, std::move(rule));
-}
-
-void RuleService::remove(unsigned position)
-{
-    assert(position < m_rules.size());
-
-    m_rules.erase(m_rules.begin() + position);
-}
-
-bool RuleService::solve(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 server={}, channel={}, origin={}, plugin={}, event={}"_format(server, channel,
-           origin, plugin, event));
-
-    int i = 0;
-    for (const Rule &rule : m_rules) {
-        log::debug() << "  candidate " << i++ << ":\n"
-                 << "    servers: " << util::join(rule.servers().begin(), rule.servers().end()) << "\n"
-                 << "    channels: " << util::join(rule.channels().begin(), rule.channels().end()) << "\n"
-                 << "    origins: " << util::join(rule.origins().begin(), rule.origins().end()) << "\n"
-                 << "    plugins: " << util::join(rule.plugins().begin(), rule.plugins().end()) << "\n"
-                 << "    events: " << util::join(rule.events().begin(), rule.events().end()) << "\n"
-                 << "    action: " << ((rule.action() == RuleAction::Accept) ? "accept" : "drop") << std::endl;
-
-        if (rule.match(server, channel, origin, plugin, event))
-            result = rule.action() == RuleAction::Accept;
-    }
-
-    return result;
-}
-
-} // !irccd
--- a/lib/irccd/service-rule.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * service-rule.hpp -- store and solve rules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_RULE_HPP
-#define IRCCD_SERVICE_RULE_HPP
-
-/**
- * \file service-rule.hpp
- * \brief Store and solve rules.
- */
-
-#include <vector>
-
-#include "rule.hpp"
-
-namespace irccd {
-
-/**
- * \brief Store and solve rules.
- * \ingroup services
- */
-class RuleService {
-private:
-    std::vector<Rule> m_rules;
-
-public:
-    /**
-     * Get the list of rules.
-     *
-     * \return the list of rules
-     */
-    inline const std::vector<Rule> &list() const noexcept
-    {
-        return m_rules;
-    }
-
-    /**
-     * Get the number of rules.
-     *
-     * \return the number of rules
-     */
-    inline std::size_t length() const noexcept
-    {
-        return m_rules.size();
-    }
-
-    /**
-     * Append a rule.
-     *
-     * \param rule the rule to append
-     */
-    IRCCD_EXPORT void add(Rule rule);
-
-    /**
-     * Insert a new rule at the specified position.
-     *
-     * \param rule the rule
-     * \param position the position
-     */
-    IRCCD_EXPORT void insert(Rule rule, unsigned position);
-
-    /**
-     * Remove a new rule from the specified position.
-     *
-     * \pre position must be valid
-     * \param position the position
-     */
-    IRCCD_EXPORT void remove(unsigned position);
-
-    /**
-     * Resolve the action to execute with the specified 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
-     */
-    IRCCD_EXPORT bool solve(const std::string &server,
-                            const std::string &channel,
-                            const std::string &origin,
-                            const std::string &plugin,
-                            const std::string &event) noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_RULE_HPP
--- a/lib/irccd/service-server.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,592 +0,0 @@
-/*
- * service-server.cpp -- manage IRC servers
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <algorithm>
-
-#include <json.hpp>
-#include <format.h>
-
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "plugin.hpp"
-#include "server.hpp"
-#include "service-plugin.hpp"
-#include "service-rule.hpp"
-#include "service-server.hpp"
-#include "service-transport.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-using namespace nlohmann;
-
-namespace irccd {
-
-class EventHandler {
-public:
-    std::string server;
-    std::string origin;
-    std::string target;
-    std::function<std::string (Plugin &)> functionName;
-    std::function<void (Plugin &)> functionExec;
-
-    void operator()(Irccd &irccd) const
-    {
-        for (auto &plugin : irccd.plugins().list()) {
-            auto eventname = functionName(*plugin);
-            auto allowed = irccd.rules().solve(server, target, origin, plugin->name(), eventname);
-
-            if (!allowed) {
-                log::debug() << "rule: event skipped on match" << std::endl;
-                continue;
-            } else
-                log::debug() << "rule: event allowed" << std::endl;
-
-            // TODO: server-event must not know which type of plugin.
-            // TODO: get generic error.
-            try {
-                functionExec(*plugin);
-            } catch (const Exception &info) {
-                log::warning() << "plugin " << plugin->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;
-            } catch (const std::exception &ex) {
-                log::warning() << "plugin " << plugin->name() << ": error: " << ex.what() << std::endl;
-            }
-        }
-    }
-};
-
-void ServerService::handleChannelMode(const ChannelModeEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onChannelMode:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  mode: " << ev.mode << "\n";
-    log::debug() << "  argument: " << ev.argument << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onChannelMode"     },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "mode",       ev.mode             },
-        { "argument",   ev.argument         }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onChannelMode";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onChannelMode(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleChannelNotice(const ChannelNoticeEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onChannelNotice:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onChannelNotice"   },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "message",    ev.message          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onChannelNotice";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onChannelNotice(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleConnect(const ConnectEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onConnect" << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onConnect"         },
-        { "server",     ev.server->name()   }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "",
-        [=] (Plugin &) -> std::string {
-            return "onConnect";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onConnect(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleInvite(const InviteEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onInvite:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  target: " << ev.nickname << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onInvite"          },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onInvite";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onInvite(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleJoin(const JoinEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onJoin:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onJoin"            },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onJoin";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onJoin(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleKick(const KickEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onKick:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  target: " << ev.target << "\n";
-    log::debug() << "  reason: " << ev.reason << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onKick"            },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "target",     ev.target           },
-        { "reason",     ev.reason           }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onKick";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onKick(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleMessage(const MessageEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onMessage:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onMessage"         },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "message",    ev.message          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &plugin) -> std::string {
-            return util::parseMessage(ev.message, ev.server->commandCharacter(), plugin.name()).second == util::MessageType::Command ? "onCommand" : "onMessage";
-        },
-        [=] (Plugin &plugin) mutable {
-            auto copy = ev;
-            auto pack = util::parseMessage(copy.message, copy.server->commandCharacter(), plugin.name());
-
-            copy.message = pack.first;
-
-            if (pack.second == util::MessageType::Command)
-                plugin.onCommand(m_irccd, copy);
-            else
-                plugin.onMessage(m_irccd, copy);
-        }
-    });
-}
-
-void ServerService::handleMe(const MeEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onMe:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  target: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onMe"              },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "target",     ev.channel          },
-        { "message",    ev.message          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onMe";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onMe(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleMode(const ModeEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onMode\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  mode: " << ev.mode << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onMode"            },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "mode",       ev.mode             }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
-        [=] (Plugin &) -> std::string {
-            return "onMode";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onMode(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleNames(const NamesEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onNames:\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  names: " << util::join(ev.names.begin(), ev.names.end(), ", ") << std::endl;
-
-    auto names = json::array();
-
-    for (const auto &v : ev.names)
-        names.push_back(v);
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onNames"           },
-        { "server",     ev.server->name()   },
-        { "channel",    ev.channel          },
-        { "names",      std::move(names)    }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onNames";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onNames(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleNick(const NickEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onNick:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  nickname: " << ev.nickname << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onNick"            },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "nickname",   ev.nickname         }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
-        [=] (Plugin &) -> std::string {
-            return "onNick";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onNick(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleNotice(const NoticeEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onNotice:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onNotice"          },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "message",    ev.message          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
-        [=] (Plugin &) -> std::string {
-            return "onNotice";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onNotice(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handlePart(const PartEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onPart:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  reason: " << ev.reason << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onPart"            },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "reason",     ev.reason           }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onPart";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onPart(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleQuery(const QueryEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onQuery:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onQuery"           },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "message",    ev.message          }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
-        [=] (Plugin &plugin) -> std::string {
-            return util::parseMessage(ev.message, ev.server->commandCharacter(), plugin.name()).second == util::MessageType::Command ? "onQueryCommand" : "onQuery";
-        },
-        [=] (Plugin &plugin) mutable {
-            auto copy = ev;
-            auto pack = util::parseMessage(copy.message, copy.server->commandCharacter(), plugin.name());
-
-            copy.message = pack.first;
-
-            if (pack.second == util::MessageType::Command)
-                plugin.onQueryCommand(m_irccd, copy);
-            else
-                plugin.onQuery(m_irccd, copy);
-        }
-    });
-}
-
-void ServerService::handleTopic(const TopicEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onTopic:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  topic: " << ev.topic << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onTopic"           },
-        { "server",     ev.server->name()   },
-        { "origin",     ev.origin           },
-        { "channel",    ev.channel          },
-        { "topic",      ev.topic            }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
-        [=] (Plugin &) -> std::string {
-            return "onTopic";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onTopic(m_irccd, ev);
-        }
-    });
-}
-
-void ServerService::handleWhois(const WhoisEvent &ev)
-{
-    log::debug() << "server " << ev.server->name() << ": event onWhois\n";
-    log::debug() << "  nickname: " << ev.whois.nick << "\n";
-    log::debug() << "  username: " << ev.whois.user << "\n";
-    log::debug() << "  host: " << ev.whois.host << "\n";
-    log::debug() << "  realname: " << ev.whois.realname << "\n";
-    log::debug() << "  channels: " << util::join(ev.whois.channels.begin(), ev.whois.channels.end()) << std::endl;
-
-    m_irccd.transports().broadcast(nlohmann::json::object({
-        { "event",      "onWhois"           },
-        { "server",     ev.server->name()   },
-        { "nickname",   ev.whois.nick       },
-        { "username",   ev.whois.user       },
-        { "host",       ev.whois.host       },
-        { "realname",   ev.whois.realname   }
-    }));
-
-    m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "",
-        [=] (Plugin &) -> std::string {
-            return "onWhois";
-        },
-        [=] (Plugin &plugin) {
-            plugin.onWhois(m_irccd, ev);
-        }
-    });
-}
-
-ServerService::ServerService(Irccd &irccd)
-    : m_irccd(irccd)
-{
-}
-
-void ServerService::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    for (auto &server : m_servers) {
-        server->update();
-        server->prepare(in, out, max);
-    }
-}
-
-void ServerService::sync(fd_set &in, fd_set &out)
-{
-    for (auto &server : m_servers)
-        server->sync(in, out);
-}
-
-bool ServerService::has(const std::string &name) const noexcept
-{
-    return std::count_if(m_servers.cbegin(), m_servers.end(), [&] (const auto &server) {
-        return server->name() == name;
-    }) > 0;
-}
-
-void ServerService::add(std::shared_ptr<Server> server)
-{
-    assert(!has(server->name()));
-
-    using namespace std::placeholders;
-
-    std::weak_ptr<Server> ptr(server);
-
-    server->onChannelMode.connect(std::bind(&ServerService::handleChannelMode, this, _1));
-    server->onChannelNotice.connect(std::bind(&ServerService::handleChannelNotice, this, _1));
-    server->onConnect.connect(std::bind(&ServerService::handleConnect, this, _1));
-    server->onInvite.connect(std::bind(&ServerService::handleInvite, this, _1));
-    server->onJoin.connect(std::bind(&ServerService::handleJoin, this, _1));
-    server->onKick.connect(std::bind(&ServerService::handleKick, this, _1));
-    server->onMessage.connect(std::bind(&ServerService::handleMessage, this, _1));
-    server->onMe.connect(std::bind(&ServerService::handleMe, this, _1));
-    server->onMode.connect(std::bind(&ServerService::handleMode, this, _1));
-    server->onNames.connect(std::bind(&ServerService::handleNames, this, _1));
-    server->onNick.connect(std::bind(&ServerService::handleNick, this, _1));
-    server->onNotice.connect(std::bind(&ServerService::handleNotice, this, _1));
-    server->onPart.connect(std::bind(&ServerService::handlePart, this, _1));
-    server->onQuery.connect(std::bind(&ServerService::handleQuery, this, _1));
-    server->onTopic.connect(std::bind(&ServerService::handleTopic, this, _1));
-    server->onWhois.connect(std::bind(&ServerService::handleWhois, this, _1));
-    server->onDie.connect([this, ptr] () {
-        m_irccd.post([=] (Irccd &) {
-            auto server = ptr.lock();
-
-            if (server) {
-                log::info("server {}: removed"_format(server->name()));
-                m_servers.erase(std::find(m_servers.begin(), m_servers.end(), server));
-            }
-        });
-    });
-
-    m_servers.push_back(std::move(server));
-}
-
-std::shared_ptr<Server> ServerService::get(const std::string &name) const noexcept
-{
-    auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
-        return server->name() == name;
-    });
-
-    if (it == m_servers.end())
-        return nullptr;
-
-    return *it;
-}
-
-std::shared_ptr<Server> ServerService::require(const std::string &name) const
-{
-    auto server = get(name);
-
-    if (!server)
-        throw std::invalid_argument("server {} not found"_format(name));
-
-    return server;
-}
-
-void ServerService::remove(const std::string &name)
-{
-    auto it = std::find_if(m_servers.begin(), m_servers.end(), [&] (const auto &server) {
-        return server->name() == name;
-    });
-
-    if (it != m_servers.end()) {
-        (*it)->disconnect();
-        m_servers.erase(it);
-    }
-}
-
-void ServerService::clear() noexcept
-{
-    for (auto &server : m_servers)
-        server->disconnect();
-
-    m_servers.clear();
-}
-
-} // !irccd
--- a/lib/irccd/service-server.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/*
- * service-server.hpp -- manage IRC servers
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_SERVER_HPP
-#define IRCCD_SERVICE_SERVER_HPP
-
-/**
- * \file service-server.hpp
- * \brief Manage IRC servers.
- */
-
-#include <memory>
-#include <set>
-#include <string>
-
-#include "server.hpp"
-
-namespace irccd {
-
-class Irccd;
-
-/**
- * \brief Manage IRC servers.
- * \ingroup services
- */
-class ServerService {
-private:
-    Irccd &m_irccd;
-    std::vector<std::shared_ptr<Server>> m_servers;
-
-    void handleChannelMode(const ChannelModeEvent &);
-    void handleChannelNotice(const ChannelNoticeEvent &);
-    void handleConnect(const ConnectEvent &);
-    void handleInvite(const InviteEvent &);
-    void handleJoin(const JoinEvent &);
-    void handleKick(const KickEvent &);
-    void handleMessage(const MessageEvent &);
-    void handleMe(const MeEvent &);
-    void handleMode(const ModeEvent &);
-    void handleNames(const NamesEvent &);
-    void handleNick(const NickEvent &);
-    void handleNotice(const NoticeEvent &);
-    void handlePart(const PartEvent &);
-    void handleQuery(const QueryEvent &);
-    void handleTopic(const TopicEvent &);
-    void handleWhois(const WhoisEvent &);
-
-public:
-    /**
-     * Create the server service.
-     */
-    IRCCD_EXPORT ServerService(Irccd &instance);
-
-    /**
-     * \copydoc Service::prepare
-     */
-    IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * \copydoc Service::sync
-     */
-    IRCCD_EXPORT void sync(fd_set &in, fd_set &out);
-
-    /**
-     * Get the list of servers
-     *
-     * \return the servers
-     */
-    inline const std::vector<std::shared_ptr<Server>> &servers() const noexcept
-    {
-        return m_servers;
-    }
-
-    /**
-     * Check if a server exists.
-     *
-     * \param name the name
-     * \return true if exists
-     */
-    IRCCD_EXPORT bool has(const std::string &name) const noexcept;
-
-    /**
-     * Add a new server to the application.
-     *
-     * \pre hasServer must return false
-     * \param sv the server
-     */
-    IRCCD_EXPORT void add(std::shared_ptr<Server> sv);
-
-    /**
-     * Get a server or empty one if not found
-     *
-     * \param name the server name
-     * \return the server or empty one if not found
-     */
-    IRCCD_EXPORT std::shared_ptr<Server> get(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
-     */
-    IRCCD_EXPORT std::shared_ptr<Server> require(const std::string &name) const;
-
-    /**
-     * Remove a server from the irccd instance.
-     *
-     * The server if any, will be disconnected.
-     *
-     * \param name the server name
-     */
-    IRCCD_EXPORT void remove(const std::string &name);
-
-    /**
-     * Remove all servers.
-     *
-     * All servers will be disconnected.
-     */
-    IRCCD_EXPORT void clear() noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_SERVER_HPP
--- a/lib/irccd/service-transport.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * service-transport.cpp -- manage transport servers and clients
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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.hpp"
-#include "irccd.hpp"
-#include "json.hpp"
-#include "logger.hpp"
-#include "service-command.hpp"
-#include "service-transport.hpp"
-#include "transport.hpp"
-
-namespace irccd {
-
-void TransportService::handleCommand(std::weak_ptr<TransportClient> ptr, const nlohmann::json &object)
-{
-    assert(object.is_object());
-
-    m_irccd.post([=] (Irccd &) {
-        // 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->is_string()) {
-            // TODO: send error.
-            log::warning("invalid command object");
-            return;
-        }
-
-        // 2. Search for a command
-        auto cmd = m_irccd.commands().find(*name);
-
-        if (!cmd) {
-            // TODO: send error again.
-            log::warning("command does not exists");
-            return;
-        }
-
-        // 3. Try to execute it.
-        auto response = nlohmann::json::object({});
-
-        try {
-            response = cmd->exec(m_irccd, object);
-
-            // Adjust if command has returned something else.
-            if (!response.is_object())
-                response = nlohmann::json::object({});
-
-            response.push_back({"status", true});
-        } catch (const std::exception &ex) {
-            response.push_back({"status", false});
-            response.push_back({"error", ex.what()});
-        }
-
-        // 4. Store the command name result.
-        response.push_back({"response", *name});
-
-        // 5. Send the result.
-        tc->send(response);
-    });
-}
-
-void TransportService::handleDie(std::weak_ptr<TransportClient> ptr)
-{
-    m_irccd.post([=] (Irccd &) {
-        log::info("transport: client disconnected");
-
-        auto tc = ptr.lock();
-
-        if (tc)
-            m_clients.erase(std::find(m_clients.begin(), m_clients.end(), tc));
-    });
-}
-
-TransportService::TransportService(Irccd &irccd) noexcept
-    : m_irccd(irccd)
-{
-}
-
-void TransportService::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    // Add transport servers.
-    for (const auto &transport : m_servers) {
-        FD_SET(transport->handle(), &in);
-
-        if (transport->handle() > max)
-            max = transport->handle();
-    }
-
-    // Transport clients.
-    for (const auto &client : m_clients)
-        client->prepare(in, out, max);
-}
-
-void TransportService::sync(fd_set &in, fd_set &out)
-{
-    using namespace std::placeholders;
-
-    // Transport clients.
-    for (const auto &client : m_clients) {
-        try {
-            client->sync(in, out);
-        } catch (const std::exception &ex) {
-            log::info() << "transport: client disconnected: " << ex.what() << std::endl;
-            handleDie(client);
-        }
-    }
-
-    // Transport servers.
-    for (const auto &transport : m_servers) {
-        if (!FD_ISSET(transport->handle(), &in))
-            continue;
-
-        log::debug("transport: new client connected");
-
-        std::shared_ptr<TransportClient> client = transport->accept();
-        std::weak_ptr<TransportClient> ptr(client);
-
-        try {
-            // Connect signals.
-            client->onCommand.connect(std::bind(&TransportService::handleCommand, this, ptr, _1));
-            client->onDie.connect(std::bind(&TransportService::handleDie, this, ptr));
-
-            // Register it.
-            m_clients.push_back(std::move(client));
-        } catch (const std::exception &ex) {
-            log::info() << "transport: client disconnected: " << ex.what() << std::endl;
-        }
-    }
-}
-
-void TransportService::add(std::shared_ptr<TransportServer> ts)
-{
-    m_servers.push_back(std::move(ts));
-}
-
-void TransportService::broadcast(const nlohmann::json &json)
-{
-    assert(json.is_object());
-
-    for (const auto &client : m_clients)
-        if (client->state() == TransportClient::Ready)
-            client->send(json);
-}
-
-} // !irccd
--- a/lib/irccd/service-transport.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * service-transport.hpp -- manage transport servers and clients
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_TRANSPORT_HPP
-#define IRCCD_SERVICE_TRANSPORT_HPP
-
-/**
- * \file service-transport.hpp
- * \brief manage transport servers and clients.
- */
-
-#include <json.hpp>
-
-namespace irccd {
-
-class TransportServer;
-class TransportClient;
-
-/**
- * \brief manage transport servers and clients.
- * \ingroup services
- */
-class TransportService {
-private:
-    Irccd &m_irccd;
-
-    std::vector<std::shared_ptr<TransportServer>> m_servers;
-    std::vector<std::shared_ptr<TransportClient>> m_clients;
-
-    void handleCommand(std::weak_ptr<TransportClient>, const nlohmann::json &);
-    void handleDie(std::weak_ptr<TransportClient>);
-
-public:
-    /**
-     * Create the transport service.
-     *
-     * \param irccd the irccd instance
-     */
-    IRCCD_EXPORT TransportService(Irccd &irccd) noexcept;
-
-    /**
-     * \copydoc Service::prepare
-     */
-    IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * \copydoc Service::sync
-     */
-    IRCCD_EXPORT void sync(fd_set &in, fd_set &out);
-
-    /**
-     * Add a transport server.
-     *
-     * \param ts the transport server
-     */
-    IRCCD_EXPORT void add(std::shared_ptr<TransportServer> ts);
-
-    /**
-     * Send data to all clients.
-     *
-     * \pre object.is_object()
-     * \param object the json object
-     */
-    IRCCD_EXPORT void broadcast(const nlohmann::json &object);
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_TRANSPORT_HPP
--- a/lib/irccd/signals.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +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.hpp
- * \brief Similar Qt signal subsystem for irccd
- */
-
-namespace irccd {
-
-/**
- * \class SignalConnection
- * \brief Stores the reference to the callable
- *
- * This class can be stored to remove a registered function from a Signal, be careful to not mix connections between different signals as
- * they are just referenced by ids.
- */
-class SignalConnection {
-private:
-    unsigned m_id;
-
-public:
-    /**
-     * Create a signal connection.
-     *
-     * \param id the id
-     */
-    inline SignalConnection(unsigned id) noexcept
-        : m_id(id)
-    {
-    }
-
-    /**
-     * Get the reference object.
-     *
-     * \return the id
-     */
-    inline unsigned id() const noexcept
-    {
-        return m_id;
-    }
-};
-
-/**
- * \class Signal
- * \brief Stores and call registered functions
- *
- * This class is intended to be use as a public field in the desired object.
- *
- * The user just have to call one of connect(), disconnect() or the call
- * operator to use this class.
- *
- * It stores the callable as std::function so type-erasure is complete.
- *
- * The user is responsible of taking care that the object is still alive
- * in case that the function takes a reference to the object.
- */
-template <typename... Args>
-class Signal {
-private:
-    using Function = std::function<void (Args...)>;
-    using FunctionMap = std::unordered_map<unsigned, Function>;
-    using Stack = std::stack<unsigned>;
-
-    FunctionMap m_functions;
-    Stack m_stack;
-    unsigned m_max{0};
-
-public:
-    /**
-     * Register a new function to the signal.
-     *
-     * \param function the function
-     * \return the connection in case you want to remove it
-     */
-    inline SignalConnection connect(Function function) noexcept
-    {
-        unsigned id;
-
-        if (!m_stack.empty()) {
-            id = m_stack.top();
-            m_stack.pop();
-        } else
-            id = m_max ++;
-
-        m_functions.emplace(id, std::move(function));
-
-        return SignalConnection{id};
-    }
-
-    /**
-     * Disconnect a connection.
-     *
-     * \param connection the connection
-     * \warning Be sure that the connection belongs to that signal
-     */
-    inline void disconnect(const SignalConnection &connection) noexcept
-    {
-        auto value = m_functions.find(connection.id());
-
-        if (value != m_functions.end()) {
-            m_functions.erase(connection.id());
-            m_stack.push(connection.id());
-        }
-    }
-
-    /**
-     * Remove all registered functions.
-     */
-    inline void clear()
-    {
-        m_functions.clear();
-        m_max = 0;
-
-        while (!m_stack.empty())
-            m_stack.pop();
-    }
-
-    /**
-     * Call every functions.
-     *
-     * \param args the arguments to pass to the signal
-     */
-    void operator()(Args... args) const
-    {
-        /*
-         * Make a copy of the ids before iterating because the callbacks may eventually remove or modify the list.
-         */
-        std::vector<unsigned> ids;
-
-        for (auto &pair : m_functions)
-            ids.push_back(pair.first);
-
-        /*
-         * Now iterate while checking if the next id is still available, however if any new signals were added while iterating, they
-         * will not be called immediately.
-         */
-        for (unsigned i : ids) {
-            auto it = m_functions.find(i);
-
-            if (it != m_functions.end())
-                it->second(args...);
-        }
-    }
-};
-
-} // !irccd
-
-#endif // !IRCCD_SIGNALS_H
--- a/lib/irccd/system.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +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 "sysconfig.hpp"
-
-#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 "fs.hpp"
-#include "logger.hpp"
-#include "system.hpp"
-#include "util.hpp"
-
-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.
- *
- *   - typeName the type of id (uid or gid)
- *   - value the value (numeric or name)
- *   - lookup the lookup function to resolve the name (getpwnam or getgrnam)
- *   - setter the function to apply the id (setuid or setgid)
- *   - 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
-    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/lib/irccd/system.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * system.hpp -- 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_HPP
-#define IRCCD_SYSTEM_HPP
-
-/**
- * \file system.hpp
- * \brief System dependant functions
- */
-
-#include <cstdint>
-#include <string>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * \brief Namespace for system functions.
- */
-namespace sys {
-
-/**
- * Set the program name, needed for some functions or some systems.
- *
- * \param name the program name
- */
-IRCCD_EXPORT void setProgramName(std::string name) noexcept;
-
-/**
- * Get the program name.
- *
- * \return the program name
- */
-IRCCD_EXPORT const std::string &programName() noexcept;
-
-/**
- * Get the system name.
- *
- * \return the name
- */
-IRCCD_EXPORT std::string name();
-
-/**
- * Get the system version.
- *
- * \return the version
- */
-IRCCD_EXPORT std::string version();
-
-/**
- * Get the number of seconds elapsed since the boottime.
- *
- * \return the number of seconds
- */
-IRCCD_EXPORT uint64_t uptime();
-
-/**
- * Get the milliseconds elapsed since the application
- * startup.
- *
- * \return the milliseconds
- */
-IRCCD_EXPORT uint64_t ticks();
-
-/**
- * Get an environment variable.
- *
- * \return the value or empty string
- */
-IRCCD_EXPORT std::string env(const std::string &var);
-
-/**
- * Get home directory usually /home/foo
- *
- * \return the home directory
- */
-IRCCD_EXPORT std::string home();
-
-#if defined(HAVE_SETUID)
-
-/**
- * Set the effective uid by name or numeric value.
- *
- * \param value the value
- */
-IRCCD_EXPORT void setUid(const std::string &value);
-
-#endif
-
-#if defined(HAVE_SETGID)
-
-/**
- * Set the effective gid by name or numeric value.
- *
- * \param value the value
- */
-IRCCD_EXPORT void setGid(const std::string &value);
-
-#endif
-
-} // !sys
-
-} // !irccd
-
-#endif // !IRCCD_SYSTEM_HPP
--- a/lib/irccd/timer.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +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.hpp"
-
-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(std::bind(&Timer::run, this))
-{
-}
-
-Timer::~Timer()
-{
-    assert(m_state != Running);
-
-    try {
-        m_state = Stopped;
-        m_condition.notify_one();
-        m_thread.join();
-    } catch (...) {
-    }
-}
-
-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/lib/irccd/timer.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/*
- * timer.hpp -- 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_HPP
-#define IRCCD_TIMER_HPP
-
-/**
- * \file timer.hpp
- * \brief Provides interval based timers for JavaScript
- */
-
-#include <atomic>
-#include <condition_variable>
-#include <functional>
-#include <mutex>
-#include <thread>
-
-#include "signals.hpp"
-#include "sysconfig.hpp"
-
-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
-     */
-    IRCCD_EXPORT Timer(TimerType type, unsigned delay) noexcept;
-
-    /**
-     * Destructor, closes the thread.
-     *
-     * \pre stop() must have been called.
-     */
-    IRCCD_EXPORT 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
-     */
-    IRCCD_EXPORT void start();
-
-    /**
-     * Stop the timer, may be used by the user to stop it.
-     *
-     * \note Thread-safe
-     */
-    IRCCD_EXPORT 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_HPP
--- a/lib/irccd/transport.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,403 +0,0 @@
-/*
- * transport.cpp -- irccd transports
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <cstdio>
-
-#include "transport.hpp"
-
-namespace irccd {
-
-/*
- * TransportClient
- * ------------------------------------------------------------------
- */
-
-void TransportClient::error(const std::string &msg)
-{
-    m_state = Closing;
-
-    send({{ "error", msg }});
-}
-
-void TransportClient::flush() noexcept
-{
-    for (std::size_t pos; (pos = m_input.find("\r\n\r\n")) != std::string::npos; ) {
-        auto message = m_input.substr(0, pos);
-
-        m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
-
-        try {
-            auto document = nlohmann::json::parse(message);
-
-            if (!document.is_object())
-                error("invalid argument");
-            else
-                onCommand(document);
-        } catch (const std::exception &ex) {
-            error(ex.what());
-        }
-    }
-}
-
-void TransportClient::authenticate() noexcept
-{
-    auto pos = m_input.find("\r\n\r\n");
-
-    if (pos == std::string::npos)
-        return;
-
-    auto msg = m_input.substr(0, pos);
-
-    m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
-
-    try {
-        auto doc = nlohmann::json::parse(msg);
-
-        if (!doc.is_object())
-            error("invalid argument");
-
-        auto cmd = doc.find("command");
-
-        if (cmd == doc.end() || !cmd->is_string() || *cmd != "auth")
-            error("authentication required");
-
-        auto pw = doc.find("password");
-        auto result = true;
-
-        if (pw == doc.end() || !pw->is_string() || *pw != m_parent.password()) {
-            m_state = Closing;
-            result = false;
-        } else
-            m_state = Ready;
-
-        send({
-            { "response", "auth" },
-            { "result", result }
-        });
-    } catch (const std::exception &ex) {
-        error(ex.what());
-    }
-}
-
-void TransportClient::recv() noexcept
-{
-    try {
-        std::string buffer;
-
-        buffer.resize(512);
-        buffer.resize(recv(&buffer[0], buffer.size()));
-
-        if (buffer.empty())
-            onDie();
-
-        m_input += std::move(buffer);
-    } catch (const std::exception &) {
-        onDie();
-    }
-}
-
-void TransportClient::send() noexcept
-{
-    try {
-        auto ns = send(&m_output[0], m_output.size());
-
-        if (ns == 0)
-            onDie();
-
-        m_output.erase(0, ns);
-    } catch (const std::exception &ex) {
-        onDie();
-    }
-}
-
-unsigned TransportClient::recv(void *buffer, unsigned length)
-{
-    return m_socket.recv(buffer, length);
-}
-
-unsigned TransportClient::send(const void *buffer, unsigned length)
-{
-    return m_socket.send(buffer, length);
-}
-
-TransportClient::TransportClient(TransportServer &parent, net::TcpSocket socket)
-    : m_parent(parent)
-    , m_socket(std::move(socket))
-{
-    assert(m_socket.isOpen());
-
-    m_socket.set(net::option::SockBlockMode(false));
-
-    // Send some information.
-    auto object = nlohmann::json::object({
-        { "program",    "irccd"                 },
-        { "major",      IRCCD_VERSION_MAJOR     },
-        { "minor",      IRCCD_VERSION_MINOR     },
-        { "patch",      IRCCD_VERSION_PATCH     }
-    });
-
-#if defined(WITH_JS)
-    object.push_back({"javascript", true});
-#endif
-#if defined(WITH_SSL)
-    object.push_back({"ssl", true});
-#endif
-
-    send(object);
-}
-
-void TransportClient::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    if (m_socket.handle() > max)
-        max = m_socket.handle();
-
-    switch (m_state) {
-    case Greeting:
-        FD_SET(m_socket.handle(), &out);
-        break;
-    case Authenticating:
-        FD_SET(m_socket.handle(), &in);
-        break;
-    case Ready:
-        FD_SET(m_socket.handle(), &in);
-
-        if (!m_output.empty())
-            FD_SET(m_socket.handle(), &out);
-        break;
-    case Closing:
-        if (!m_output.empty())
-            FD_SET(m_socket.handle(), &out);
-        else
-            onDie();
-        break;
-    default:
-        break;
-    }
-}
-
-void TransportClient::sync(fd_set &in, fd_set &out)
-{
-    switch (m_state) {
-    case Greeting:
-        send();
-
-        if (m_output.empty())
-            m_state = m_parent.password().empty() ? Ready : Authenticating;
-
-        break;
-    case Authenticating:
-        if (FD_ISSET(m_socket.handle(), &in))
-            recv();
-
-        authenticate();
-        break;
-    case Ready:
-        if (FD_ISSET(m_socket.handle(), &in))
-            recv();
-        if (FD_ISSET(m_socket.handle(), &out))
-            send();
-
-        flush();
-        break;
-    case Closing:
-        if (FD_ISSET(m_socket.handle(), &out))
-            send();
-        break;
-    default:
-        break;
-    }
-}
-
-void TransportClient::send(const nlohmann::json &json)
-{
-    assert(json.is_object());
-
-    m_output += json.dump();
-    m_output += "\r\n\r\n";
-}
-
-/*
- * TransportClientTls
- * ------------------------------------------------------------------
- */
-
-void TransportClientTls::handshake()
-{
-    try {
-        m_ssl.handshake();
-        m_handshake = HandshakeReady;
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    } catch (const std::exception &) {
-        onDie();
-    }
-}
-
-TransportClientTls::TransportClientTls(const std::string &pkey,
-                                       const std::string &cert,
-                                       TransportServer &server,
-                                       net::TcpSocket socket)
-    : TransportClient(server, std::move(socket))
-    , m_ssl(m_socket)
-{
-    m_ssl.setPrivateKey(pkey);
-    m_ssl.setCertificate(cert);
-
-    handshake();
-}
-
-unsigned TransportClientTls::recv(void *buffer, unsigned length)
-{
-    unsigned nread = 0;
-
-    try {
-        nread = m_ssl.recv(buffer, length);
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    }
-
-    return nread;
-}
-
-unsigned TransportClientTls::send(const void *buffer, unsigned length)
-{
-    unsigned nsent = 0;
-
-    try {
-        nsent = m_ssl.send(buffer, length);
-    } catch (const net::WantReadError &) {
-        m_handshake = HandshakeRead;
-    } catch (const net::WantWriteError &) {
-        m_handshake = HandshakeWrite;
-    }
-
-    return nsent;
-}
-
-void TransportClientTls::prepare(fd_set &in, fd_set &out, net::Handle &max)
-{
-    if (m_socket.handle() > max)
-        max = m_socket.handle();
-
-    switch (m_handshake) {
-    case HandshakeRead:
-        FD_SET(m_socket.handle(), &in);
-        break;
-    case HandshakeWrite:
-        FD_SET(m_socket.handle(), &out);
-        break;
-    default:
-        TransportClient::prepare(in, out, max);
-        break;
-    }
-}
-
-void TransportClientTls::sync(fd_set &in, fd_set &out)
-{
-    switch (m_handshake) {
-    case HandshakeRead:
-    case HandshakeWrite:
-        handshake();
-        break;
-    default:
-        TransportClient::sync(in, out);
-    }
-}
-
-/*
- * TransportServerIp
- * ------------------------------------------------------------------
- */
-
-TransportServerIp::TransportServerIp(const std::string &address,
-                                     std::uint16_t port,
-                                     std::uint8_t mode)
-    : TransportServer(net::TcpSocket((mode & v6) ? AF_INET6 : AF_INET, 0))
-{
-    assert((mode & v6) || (mode & v4));
-
-    m_socket.set(net::option::SockReuseAddress(true));
-
-    if (mode & v6) {
-        if (address == "*")
-            m_socket.bind(net::ipv6::any(port));
-        else
-            m_socket.bind(net::ipv6::pton(address, port));
-
-        // Disable or enable IPv4 when using IPv6.
-        if (!(mode & v4))
-            m_socket.set(net::option::Ipv6Only(true));
-    } else {
-        if (address == "*")
-            m_socket.bind(net::ipv4::any(port));
-        else
-            m_socket.bind(net::ipv4::pton(address, port));
-    }
-
-    m_socket.listen();
-}
-
-/*
- * TransportServerTls
- * ------------------------------------------------------------------
- */
-
-TransportServerTls::TransportServerTls(const std::string &pkey,
-                                       const std::string &cert,
-                                       const std::string &address,
-                                       std::uint16_t port,
-                                       std::uint8_t mode)
-    : TransportServerIp(address, port, mode)
-    , m_privatekey(pkey)
-    , m_cert(cert)
-{
-}
-
-std::unique_ptr<TransportClient> TransportServerTls::accept()
-{
-    return std::make_unique<TransportClientTls>(m_privatekey, m_cert, *this, m_socket.accept());
-}
-
-/*
- * TransportServerLocal
- * ------------------------------------------------------------------
- */
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-
-TransportServerLocal::TransportServerLocal(std::string path)
-    : TransportServer(net::TcpSocket(AF_LOCAL, 0))
-    , m_path(std::move(path))
-{
-    m_socket.bind(net::local::create(m_path, true));
-    m_socket.listen();
-}
-
-TransportServerLocal::~TransportServerLocal()
-{
-    ::remove(m_path.c_str());
-}
-
-#endif
-
-} // !irccd
--- a/lib/irccd/transport.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,388 +0,0 @@
-/*
- * transport.hpp -- irccd transports
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_HPP
-#define IRCCD_TRANSPORT_HPP
-
-/**
- * \file transport.hpp
- * \brief Irccd transports.
- */
-
-#include <cstdint>
-#include <memory>
-#include <string>
-
-#include <json.hpp>
-
-#include "net.hpp"
-#include "signals.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-class TransportServer;
-
-/**
- * \class TransportClient
- * \brief Client connected to irccd.
- *
- * This class emits a warning upon clients request through onCommand signal.
- */
-class TransportClient {
-public:
-    /**
-     * \brief Client state
-     */
-    enum State {
-        Greeting,               //!< client is getting irccd info
-        Authenticating,         //!< client requires authentication
-        Ready,                  //!< client is ready to use
-        Closing                 //!< client must disconnect
-    };
-
-    /**
-     * Signal: onCommand
-     * ----------------------------------------------------------
-     *
-     * Arguments:
-     *   - the command
-     */
-    Signal<const nlohmann::json &> onCommand;
-
-    /**
-     * Signal: onDie
-     * ----------------------------------------------------------
-     *
-     * The client has disconnected.
-     */
-    Signal<> onDie;
-
-private:
-    void error(const std::string &msg);
-    void flush() noexcept;
-    void authenticate() noexcept;
-
-protected:
-    State m_state{Greeting};    //!< current client state
-    TransportServer &m_parent;  //!< parent transport server
-    net::TcpSocket m_socket;    //!< socket
-    std::string m_input;        //!< input buffer
-    std::string m_output;       //!< output buffer
-
-    /**
-     * Fill the input buffer with available data.
-     */
-    void recv() noexcept;
-
-    /**
-     * Flush the output buffer from available pending data.
-     */
-    void send() noexcept;
-
-    /**
-     * Try to receive some data into the given buffer.
-     *
-     * \param buffer the destination buffer
-     * \param length the buffer length
-     * \return the number of bytes received
-     */
-    IRCCD_EXPORT virtual unsigned recv(void *buffer, unsigned length);
-
-    /**
-     * Try to send some data into the given buffer.
-     *
-     * \param buffer the source buffer
-     * \param length the buffer length
-     * \return the number of bytes sent
-     */
-    IRCCD_EXPORT virtual unsigned send(const void *buffer, unsigned length);
-
-public:
-    /**
-     * Create a transport client from the socket.
-     *
-     * \pre socket must be valid
-     * \param parent the parent server
-     * \param socket the new socket
-     */
-    IRCCD_EXPORT TransportClient(TransportServer &parent, net::TcpSocket socket);
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~TransportClient() = default;
-
-    /**
-     * Get the client state.
-     *
-     * \return the client state
-     */
-    inline State state() const noexcept
-    {
-        return m_state;
-    }
-
-    /**
-     * Append some data to the output queue.
-     *
-     * \pre json.is_object()
-     * \param json the json object
-     */
-    IRCCD_EXPORT void send(const nlohmann::json &json);
-
-    /**
-     * \copydoc Service::prepare
-     */
-    IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * \copydoc Service::sync
-     */
-    IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out);
-};
-
-/*
- * TransportClientTls
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief TLS version of transport client.
- */
-class TransportClientTls : public TransportClient {
-private:
-    enum {
-        HandshakeWrite,
-        HandshakeRead,
-        HandshakeReady
-    } m_handshake{HandshakeReady};
-
-    net::TlsSocket m_ssl;
-
-    void handshake();
-
-protected:
-    /**
-     * \copydoc TransportClient::recv
-     */
-    unsigned recv(void *buffer, unsigned length) override;
-
-    /**
-     * \copydoc TransportClient::send
-     */
-    unsigned send(const void *buffer, unsigned length) override;
-
-public:
-    /**
-     * Create the transport client.
-     *
-     * \pre socket.isOpen()
-     * \param pkey the private key
-     * \param cert the certificate file
-     * \param socket the accepted socket
-     * \param parent the parent server
-     * \param socket the new socket
-     */
-    IRCCD_EXPORT TransportClientTls(const std::string &pkey,
-                                    const std::string &cert,
-                                    TransportServer &server,
-                                    net::TcpSocket socket);
-
-    /**
-     * \copydoc TransportClient::prepare
-     */
-    IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max);
-
-    /**
-     * \copydoc TransportClient::sync
-     */
-    IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out);
-};
-
-/*
- * 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;
-
-protected:
-    net::TcpSocket m_socket;
-    std::string m_password;
-
-public:
-    /**
-     * Default constructor.
-     */
-    inline TransportServer(net::TcpSocket socket)
-        : m_socket(std::move(socket))
-    {
-    }
-
-    /**
-     * Get the socket handle for this transport.
-     *
-     * \return the handle
-     */
-    inline net::Handle handle() const noexcept
-    {
-        return m_socket.handle();
-    }
-
-    /**
-     * Get the password.
-     *
-     * \return the password
-     */
-    inline const std::string &password() const noexcept
-    {
-        return m_password;
-    }
-
-    /**
-     * Set an optional password.
-     *
-     * \return the password
-     */
-    inline void setPassword(std::string password) noexcept
-    {
-        m_password = std::move(password);
-    }
-
-    /**
-     * Destructor defaulted.
-     */
-    virtual ~TransportServer() = default;
-
-    /**
-     * Accept a new client depending on the domain.
-     *
-     * \return the new client
-     */
-    virtual std::unique_ptr<TransportClient> accept()
-    {
-        return std::make_unique<TransportClient>(*this, m_socket.accept());
-    }
-};
-
-/**
- * \brief Create IP transport.
- */
-class TransportServerIp : public TransportServer {
-public:
-    /**
-     * \brief Domain to use.
-     */
-    enum Mode {
-        v4 = (1 << 0),      //!< IPv6
-        v6 = (1 << 1)       //!< IPv4
-    };
-
-    /**
-     * Constructor.
-     * \pre mode > 0
-     * \param address the address (* for any)
-     * \param port the port number
-     * \param mode the domains to use (can be OR'ed)
-     */
-    IRCCD_EXPORT TransportServerIp(const std::string &address,
-                                   std::uint16_t port,
-                                   std::uint8_t mode = v4);
-};
-
-/**
- * \brief TLS over IP transport.
- */
-class TransportServerTls : public TransportServerIp {
-private:
-    std::string m_privatekey;
-    std::string m_cert;
-
-public:
-    /**
-     * Constructor.
-     * \pre mode > 0
-     * \param pkey the private key file
-     * \param cert the certificate file
-     * \param address the address (* for any)
-     * \param port the port number
-     * \param mode the domains to use (can be OR'ed)
-     */
-    IRCCD_EXPORT TransportServerTls(const std::string &pkey,
-                                    const std::string &cert,
-                                    const std::string &address,
-                                    std::uint16_t port,
-                                    std::uint8_t mode = v4);
-
-    /**
-     * \copydoc TransportServer::accept
-     */
-    IRCCD_EXPORT std::unique_ptr<TransportClient> accept() override;
-};
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-
-/**
- * \brief Implementation of transports for Unix sockets.
- */
-class TransportServerLocal : public TransportServer {
-private:
-    std::string m_path;
-
-public:
-    /**
-     * Create a Unix transport.
-     *
-     * \param path the path
-     */
-    IRCCD_EXPORT TransportServerLocal(std::string path);
-
-    /**
-     * Destroy the transport and remove the file.
-     */
-    IRCCD_EXPORT ~TransportServerLocal();
-};
-
-#endif // !IRCCD_SYSTEM_WINDOWS
-
-} // !irccd
-
-#endif // !IRCCD_TRANSPORT_HPP
--- a/lib/irccd/unicode.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4796 +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.hpp"
-
-/*
- * 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]))
-
-namespace {
-
-const char32_t *rbsearch(char32_t c, const char32_t *t, int n, int ne) noexcept
-{
-   const 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;
-}
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool isspace(char32_t c) noexcept
-{
-   const char32_t *p;
-
-   p = rbsearch(c, isspacer, nelem (isspacer)/2, 2);
-
-   if (p && c >= p[0] && c <= p[1])
-       return true;
-
-   return false;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool isdigit(char32_t c) noexcept
-{
-   const char32_t *p;
-
-   p = rbsearch(c, isdigitr, nelem (isdigitr)/2, 2);
-
-   if (p && c >= p[0] && c <= p[1])
-       return true;
-
-   return false;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool isalpha(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool isupper(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool islower(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-bool istitle(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-char32_t toupper(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-char32_t tolower(char32_t c) noexcept
-{
-   const 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;
-}
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-namespace {
-
-const 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,
-};
-
-} // !namespace
-
-char32_t totitle(char32_t c) noexcept
-{
-   const 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] = static_cast<char>(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 (static_cast<unsigned char>(c) <= 127)
-        return 1;
-    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;
-}
-
-unsigned length(const std::string &str)
-{
-    unsigned 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/lib/irccd/unicode.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,273 +0,0 @@
-/*
- * unicode.hpp -- 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 UNICODE_HPP
-#define UNICODE_HPP
-
-/**
- * \file unicode.hpp
- * \brief UTF-8 to UTF-32 conversions
- * \author David Demelier <markand@malikania.fr>
- * \warning These files are auto-generated!
- */
-
-#include <stdexcept>
-#include <string>
-
-namespace irccd {
-
-/**
- * \brief Unicode namespace.
- */
-namespace unicode {
-
-/**
- * Encode the unicode code point into multibyte string.
- *
- * \param point the unicode code point
- * \param res the output buffer
- */
-void encode(char32_t point, char res[5]) noexcept;
-
-/**
- * Decode the multibyte buffer into an unicode code point.
- *
- * \param c the code point destination
- * \param res the multibyte string.
- */
-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] or -1 if invalid
- */
-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 if 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
- */
-unsigned 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
- * \param function the function callback
- * \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)
-{
-    std::string result;
-    char buffer[5];
-
-    forEach(str, [&] (char32_t code) {
-        encode(toupper(code), buffer);
-        result += buffer;
-    });
-
-    return result;
-}
-
-/**
- * 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)
-{
-    std::string result;
-    char buffer[5];
-
-    forEach(str, [&] (char32_t code) {
-        encode(tolower(code), buffer);
-        result += buffer;
-    });
-
-    return result;
-}
-
-} // !unicode
-
-} // !irccd
-
-#endif // !UNICODE_HPP
--- a/lib/irccd/util.cpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,422 +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 "sysconfig.hpp"
-
-#include <algorithm>
-#include <cassert>
-#include <cctype>
-#include <cstdlib>
-#include <ctime>
-#include <iomanip>
-#include <sstream>
-#include <stdexcept>
-
-#if defined(HAVE_POPEN)
-#include <array>
-#include <cerrno>
-#include <cstring>
-#include <functional>
-#include <memory>
-#endif
-
-#include "util.hpp"
-#include "unicode.hpp"
-
-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'  }
-};
-
-inline bool isReserved(char token) noexcept
-{
-    return token == '#' || token == '@' || token == '$' || token == '!';
-}
-
-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())
-        return std::string(1, attributesTable.at("reset"));
-
-    // Remove useless spaces.
-    std::transform(list.begin(), list.end(), list.begin(), strip);
-
-    /*
-     * 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 substituteShell(const std::string &command)
-{
-#if defined(HAVE_POPEN)
-    std::unique_ptr<FILE, std::function<int (FILE *)>> fp(popen(command.c_str(), "r"), pclose);
-
-    if (fp == nullptr)
-        throw std::runtime_error(std::strerror(errno));
-
-    std::string result;
-    std::array<char, 128> buffer;
-    std::size_t n;
-
-    while ((n = std::fread(buffer.data(), 1, 128, fp.get())) > 0)
-        result.append(buffer.data(), n);
-    if (std::ferror(fp.get()))
-        throw std::runtime_error(std::strerror(errno));
-
-    // Erase final '\n'.
-    auto it = result.find('\n');
-    if (it != std::string::npos)
-        result.erase(it);
-
-    return result;
-#else
-    throw std::runtime_error("shell template not available");
-#endif
-}
-
-std::string substitute(std::string::const_iterator &it, std::string::const_iterator &end, char token, const Substitution &params)
-{
-    assert(isReserved(token));
-
-    std::string content, value;
-
-    if (it == end)
-        return "";
-
-    while (it != end && *it != '}')
-        content += *it++;
-
-    if (it == end || *it != '}')
-        throw std::invalid_argument("unclosed "s + token + " construct"s);
-
-    it++;
-
-    // Create default original value if flag is disabled.
-    value = std::string(1, token) + "{"s + content + "}"s;
-
-    switch (token) {
-    case '#':
-        if (params.flags & Substitution::Keywords)
-            value = substituteKeywords(content, params);
-        break;
-    case '$':
-        if (params.flags & Substitution::Env)
-            value = substituteEnv(content);
-        break;
-    case '@':
-        if (params.flags & Substitution::IrcAttrs)
-            value = substituteAttributes(content);
-        break;
-    case '!':
-        if (params.flags & Substitution::Shell)
-            value = substituteShell(content);
-        break;
-    default:
-        break;
-    }
-
-    return value;
-}
-
-} // !namespace
-
-std::string format(std::string text, const Substitution &params)
-{
-    /*
-     * Change the date format before anything else to avoid interpolation with
-     * keywords and user input.
-     */
-    if (params.flags & Substitution::Date)
-        text = substituteDate(text, params);
-
-    std::ostringstream oss;
-
-    for (auto it = text.cbegin(), end = text.cend(); it != end; ) {
-        auto token = *it;
-
-        /* Is the current character a reserved token or not? */
-        if (!isReserved(token)) {
-            oss << *it++;
-            continue;
-        }
-
-        /* The token was at the end, just write it and return now. */
-        if (++it == end) {
-            oss << token;
-            continue;
-        }
-
-        /* The token is declaring a template variable, substitute it. */
-        if (*it == '{') {
-            oss << substitute(++it, end, token, params);
-            continue;
-        }
-
-        /*
-         * If the next token is different from the previous one, just let the
-         * next iteration parse the string because we can have the following
-         * constructs.
-         *
-         * "@#{var}" -> "@value"
-         */
-        if (*it != token) {
-            oss << token;
-            continue;
-        }
-
-        /*
-         * Write the token only if it's not a variable because at this step we
-         * may have the following constructs.
-         *
-         * "##" -> "##"
-         * "##hello" -> "##hello"
-         * "##{hello}" -> "#{hello}"
-         */
-        if (++it == end)
-            oss << token << token;
-        else if (*it == '{')
-            oss << token;
-    }
-
-    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;
-    std::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/lib/irccd/util.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,694 +0,0 @@
-/*
- * util.hpp -- 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_HPP
-#define IRCCD_UTIL_HPP
-
-/**
- * \file util.hpp
- * \brief Utilities.
- */
-
-#include <ctime>
-#include <initializer_list>
-#include <limits>
-#include <regex>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
-
-#include <format.h>
-#include <json.hpp>
-
-#include "net.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * \brief Namespace for utilities.
- */
-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:
-    /**
-     * \brief Disable or enable some features.
-     */
-    enum Flags {
-        Date        = (1 << 0),     //!< date templates
-        Keywords    = (1 << 1),     //!< keywords
-        Env         = (1 << 2),     //!< environment variables
-        Shell       = (1 << 3),     //!< command line command
-        IrcAttrs    = (1 << 4)      //!< IRC escape codes
-    };
-
-    /**
-     * Flags for selecting templates.
-     */
-    std::uint8_t flags{Date | Keywords | Env | IrcAttrs};
-
-    /**
-     * 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 <strong>?{}</strong> where <strong>?</strong> 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:
- *
- * - <strong>\#{name}</strong>: name will be substituted from the keywords in
- *   params,
- * - <strong>\${name}</strong>: name will be substituted from the environment
- *   variable,
- * - <strong>\@{attributes}</strong>: the attributes will be substituted to IRC
- *   colors (see below),
- * - <strong>%</strong>, 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
- *
- *   - <strong>\#{target}, welcome</strong>: if target is set to "irccd",
- *     becomes "irccd, welcome",
- *   - <strong>\@{red}\#{target}</strong>: if target is specified, it is written
- *     in red,
- *
- * ### Invalid or literals constructs
- *
- *   - <strong>\#\#{target}</strong>: will output "\#{target}",
- *   - <strong>\#\#</strong>: will output "\#\#",
- *   - <strong>\#target</strong>: will output "\#target",
- *   - <strong>\#{target</strong>: will throw std::invalid_argument.
- *
- * ### Colors & attributes
- *
- *   - <strong>\@{red,blue}</strong>: will write text red on blue background,
- *   - <strong>\@{default,yellow}</strong>: will write default color text on
- *     yellow background,
- *   - <strong>\@{white,black,bold,underline}</strong>: will write white text on
- *     black in both bold and underline.
- */
-IRCCD_EXPORT std::string format(std::string text, const Substitution &params = {});
-
-/**
- * Remove leading and trailing spaces.
- *
- * \param str the string
- * \return the removed white spaces
- */
-IRCCD_EXPORT std::string strip(std::string str);
-
-/**
- * Split a string by delimiters.
- *
- * \param list the string to split
- * \param delimiters a list of delimiters
- * \param max max number of split
- * \return a list of string splitted
- */
-IRCCD_EXPORT 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);
-}
-
-/**
- * Clamp the value between low and high.
- *
- * \param value the value
- * \param low the minimum value
- * \param high the maximum value
- * \return the value between minimum and maximum
- */
-template <typename T>
-constexpr T clamp(T value, T low, T high) noexcept
-{
-    return (value < high) ? std::max(value, low) : std::min(value, high);
-}
-
-/**
- * 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
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT bool isInt(const std::string &value, int base = 10) noexcept;
-
-/**
- * Check if the string is real.
- *
- * \param value the value
- * \return true if real
- */
-IRCCD_EXPORT 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);
-}
-
-/**
- * Tells if a number is bound between the limits.
- *
- * \param value the value to check
- * \param min the minimum
- * \param max the maximum
- * \return true if value is beyond the limits
- */
-template <typename T>
-constexpr bool isBound(T value, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) noexcept
-{
-    return value >= min && value <= max;
-}
-
-/**
- * Try to convert the string into number.
- *
- * This function will try to convert the string to number in the limits of T.
- *
- * If the string is not a number or if the converted value is out of range than
- * specified boundaries, an exception is thrown.
- *
- * By default, the function will use numeric limits from T.
- *
- * \param number the string to convert
- * \param min the minimum (defaults to T minimum)
- * \param max the maximum (defaults to T maximum)
- * \return the converted value
- * \throw std::invalid_argument if number is not a string
- * \throw std::out_of_range if the number is not between min and max
- */
-template <typename T>
-inline T toNumber(const std::string &number, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max())
-{
-    static_assert(std::is_integral<T>::value, "T must be integer type");
-
-    std::conditional_t<std::is_unsigned<T>::value, unsigned long long, long long> value;
-
-    if (std::is_unsigned<T>::value)
-        value = std::stoull(number);
-    else
-        value = std::stoll(number);
-
-    if (value < min || value > max)
-        throw std::out_of_range("out of range");
-
-    return static_cast<T>(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
- */
-IRCCD_EXPORT std::string nextNetwork(std::string &input);
-
-/**
- * Use arguments to avoid compiler warnings about unused parameters.
- */
-template <typename... Args>
-inline void unused(Args&&...) noexcept
-{
-}
-
-/**
- * Utilities for nlohmann json.
- */
-namespace json {
-
-/**
- * Require a property.
- *
- * \param json the json value
- * \param key the property name
- * \param type the requested property type
- * \return the value
- * \throw std::runtime_error if the property is missing
- */
-inline nlohmann::json require(const nlohmann::json &json, const std::string &key, nlohmann::json::value_t type)
-{
-    auto it = json.find(key);
-    auto dummy = nlohmann::json(type);
-
-    if (it == json.end())
-        throw std::runtime_error(fmt::format("missing '{}' property", key));
-    if (it->type() != type)
-        throw std::runtime_error(fmt::format("invalid '{}' property ({} expected, got {})", key, it->type_name(), dummy.type_name()));
-
-    return *it;
-}
-
-/**
- * Convenient access for booleans.
- *
- * \param json the json object
- * \param key the property key
- * \return the boolean
- * \throw std::runtime_error if the property is missing or not a boolean
- */
-inline bool requireBool(const nlohmann::json &json, const std::string &key)
-{
-    return require(json, key, nlohmann::json::value_t::boolean);
-}
-
-/**
- * Convenient access for ints.
- *
- * \param json the json object
- * \param key the property key
- * \return the int
- * \throw std::runtime_error if the property is missing or not ant int
- */
-inline std::int64_t requireInt(const nlohmann::json &json, const std::string &key)
-{
-    return require(json, key, nlohmann::json::value_t::number_integer);
-}
-
-/**
- * Convenient access for unsigned ints.
- *
- * \param json the json object
- * \param key the property key
- * \return the unsigned int
- * \throw std::runtime_error if the property is missing or not ant int
- */
-inline std::uint64_t requireUint(const nlohmann::json &json, const std::string &key)
-{
-    return require(json, key, nlohmann::json::value_t::number_unsigned);
-}
-
-/**
- * Convenient access for strings.
- *
- * \param json the json object
- * \param key the property key
- * \return the string
- * \throw std::runtime_error if the property is missing or not a string
- */
-inline std::string requireString(const nlohmann::json &json, const std::string &key)
-{
-    return require(json, key, nlohmann::json::value_t::string);
-}
-
-/**
- * Convenient access for unique identifiers.
- *
- * \param json the json object
- * \param key the property key
- * \return the identifier
- * \throw std::runtime_error if the property is invalid
- */
-inline std::string requireIdentifier(const nlohmann::json &json, const std::string &key)
-{
-    auto id = requireString(json, key);
-
-    if (!isIdentifierValid(id))
-        throw std::runtime_error("invalid '{}' identifier property");
-
-    return id;
-}
-
-/**
- * Convert the json value to boolean.
- *
- * \param json the json value
- * \param def the default value if not boolean
- * \return a boolean
- */
-inline bool toBool(const nlohmann::json &json, bool def = false) noexcept
-{
-    return json.is_boolean() ? json.get<bool>() : def;
-}
-
-/**
- * Convert the json value to int.
- *
- * \param json the json value
- * \param def the default value if not an int
- * \return an int
- */
-inline std::int64_t toInt(const nlohmann::json &json, std::int64_t def = 0) noexcept
-{
-    return json.is_number_integer() ? json.get<std::int64_t>() : def;
-}
-
-/**
- * Convert the json value to unsigned.
- *
- * \param json the json value
- * \param def the default value if not a unsigned int
- * \return an unsigned int
- */
-inline std::uint64_t toUint(const nlohmann::json &json, std::uint64_t def = 0) noexcept
-{
-    return json.is_number_unsigned() ? json.get<std::uint64_t>() : def;
-}
-
-/**
- * Convert the json value to string.
- *
- * \param json the json value
- * \param def the default value if not a string
- * \return a string
- */
-inline std::string toString(const nlohmann::json &json, std::string def = "") noexcept
-{
-    return json.is_string() ? json.get<std::string>() : def;
-}
-
-/**
- * Get a property or return null one if not found or if json is not an object.
- *
- * \param json the json value
- * \param property the property key
- * \return the value or null one if not found
- */
-inline nlohmann::json get(const nlohmann::json &json, const std::string &property) noexcept
-{
-    auto it = json.find(property);
-
-    if (it == json.end())
-        return nlohmann::json();
-
-    return *it;
-}
-
-/**
- * Convenient access for boolean with default value.
- *
- * \param json the json value
- * \param key the property key
- * \param def the default value
- * \return the boolean
- */
-inline bool getBool(const nlohmann::json &json, const std::string &key, bool def = false) noexcept
-{
-    return toBool(get(json, key), def);
-}
-
-/**
- * Convenient access for ints with default value.
- *
- * \param json the json value
- * \param key the property key
- * \param def the default value
- * \return the int
- */
-inline std::int64_t getInt(const nlohmann::json &json, const std::string &key, std::int64_t def = 0) noexcept
-{
-    return toInt(get(json, key), def);
-}
-
-/**
- * Convenient access for unsigned ints with default value.
- *
- * \param json the json value
- * \param key the property key
- * \param def the default value
- * \return the unsigned int
- */
-inline std::uint64_t getUint(const nlohmann::json &json, const std::string &key, std::uint64_t def = 0) noexcept
-{
-    return toUint(get(json, key), def);
-}
-
-/**
- * Get an integer in the given range.
- *
- * \param json the json value
- * \param key the property key
- * \param min the minimum value
- * \param max the maximum value
- * \return the value
- */
-template <typename T>
-inline T getIntRange(const nlohmann::json &json,
-                     const std::string &key,
-                     std::int64_t min = std::numeric_limits<T>::min(),
-                     std::int64_t max = std::numeric_limits<T>::max()) noexcept
-{
-    return clamp(getInt(json, key), min, max);
-}
-
-/**
- * Get an unsigned integer in the given range.
- *
- * \param json the json value
- * \param key the property key
- * \param min the minimum value
- * \param max the maximum value
- * \return value
- */
-template <typename T>
-inline T getUintRange(const nlohmann::json &json,
-                    const std::string &key,
-                    std::uint64_t min = std::numeric_limits<T>::min(),
-                    std::uint64_t max = std::numeric_limits<T>::max()) noexcept
-{
-    return clamp(getUint(json, key), min, max);
-}
-
-/**
- * Convenient access for strings with default value.
- *
- * \param json the json value
- * \param key the property key
- * \param def the default value
- * \return the string
- */
-inline std::string getString(const nlohmann::json &json, const std::string &key, std::string def = "") noexcept
-{
-    return toString(get(json, key), def);
-}
-
-} // !json
-
-/**
- * \brief Miscellaneous utilities for Pollable objects
- */
-namespace poller {
-
-/**
- * \cond HIDDEN_SYMBOLS
- */
-
-inline void prepare(fd_set &, fd_set &, net::Handle &) noexcept
-{
-}
-
-/**
- * \endcond
- */
-
-/**
- * Call prepare function for every Pollable objects.
- *
- * \param in the input set
- * \param out the output set
- * \param max the maximum handle
- * \param first the first Pollable object
- * \param rest the additional Pollable objects
- */
-template <typename Pollable, typename... Rest>
-inline void prepare(fd_set &in, fd_set &out, net::Handle &max, Pollable &first, Rest&... rest)
-{
-    first.prepare(in, out, max);
-    prepare(in, out, max, rest...);
-}
-
-/**
- * \cond HIDDEN_SYMBOLS
- */
-
-inline void sync(fd_set &, fd_set &) noexcept
-{
-}
-
-/**
- * \endcond
- */
-
-/**
- * Call sync function for every Pollable objects.
- *
- * \param in the input set
- * \param out the output set
- * \param first the first Pollable object
- * \param rest the additional Pollable objects
- */
-template <typename Pollable, typename... Rest>
-inline void sync(fd_set &in, fd_set &out, Pollable &first, Rest&... rest)
-{
-    first.sync(in, out);
-    sync(in, out, rest...);
-}
-
-/**
- * Prepare and sync Pollable objects.
- *
- * \param timeout the timeout in milliseconds (< 0 means forever)
- * \param first the the first Pollable object
- * \param rest the additional Pollable objects
- */
-template <typename Pollable, typename... Rest>
-void poll(int timeout, Pollable &first, Rest&... rest)
-{
-    fd_set in, out;
-    timeval tv = {0, timeout * 1000};
-
-    FD_ZERO(&in);
-    FD_ZERO(&out);
-
-    net::Handle max = 0;
-
-    prepare(in, out, max, first, rest...);
-
-    // Timeout or error are discarded.
-    if (::select(max + 1, &in, &out, nullptr, timeout < 0 ? nullptr : &tv) > 0)
-        sync(in, out, first, rest...);
-}
-
-} // !poller
-
-} // !util
-
-} // !irccd
-
-#endif // !IRCCD_UTIL_HPP
--- a/lib/irccd/xdg.hpp	Thu Oct 06 12:36:13 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * xdg.hpp -- XDG directory specifications
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 XDG_HPP
-#define XDG_HPP
-
-/**
- * \file xdg.hpp
- * \brief XDG directory specifications.
- * \author David Demelier <markand@malikana.fr>
- */
-
-#include <cstdlib>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * \class Xdg
- * \brief XDG directory specifications.
- *
- * Read and get XDG directories.
- *
- * This file should compiles on Windows to facilitate portability but its functions must not be used.
- */
-class Xdg {
-private:
-    std::string m_configHome;
-    std::string m_dataHome;
-    std::string m_cacheHome;
-    std::string m_runtimeDir;
-    std::vector<std::string> m_configDirs;
-    std::vector<std::string> m_dataDirs;
-
-    bool isabsolute(const std::string &path) const noexcept
-    {
-        return path.length() > 0 && path[0] == '/';
-    }
-
-    std::vector<std::string> split(const std::string &arg) const
-    {
-        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) const
-    {
-        auto value = std::getenv(var.c_str());
-
-        if (value == nullptr || !isabsolute(value)) {
-            auto home = std::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) const
-    {
-        auto value = std::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;
-    }
-
-public:
-    /**
-     * Open an xdg instance and load directories.
-     *
-     * \throw std::runtime_error on failures
-     */
-    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 = std::getenv("XDG_RUNTIME_DIR");
-        if (runtime && isabsolute(runtime))
-            m_runtimeDir = runtime;
-    }
-
-    /**
-     * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config
-     *
-     * \return the config directory
-     */
-    inline const std::string &configHome() const noexcept
-    {
-        return m_configHome;
-    }
-
-    /**
-     * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share
-     *
-     * \return the data directory
-     */
-    inline const std::string &dataHome() const noexcept
-    {
-        return m_dataHome;
-    }
-
-    /**
-     * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache
-     *
-     * \return the cache directory
-     */
-    inline const std::string &cacheHome() const noexcept
-    {
-        return m_cacheHome;
-    }
-
-    /**
-     * Get the runtime directory.
-     *
-     * There is no replacement for XDG_RUNTIME_DIR, if it is not set, an empty valus is returned and the user is
-     * responsible of using something else.
-     *
-     * \return the runtime directory
-     */
-    inline const std::string &runtimeDir() const noexcept
-    {
-        return m_runtimeDir;
-    }
-
-    /**
-     * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" }
-     *
-     * \return the list of config directories
-     */
-    inline const std::vector<std::string> &configDirs() const noexcept
-    {
-        return m_configDirs;
-    }
-
-    /**
-     * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" }
-     *
-     * \return the list of data directories
-     */
-    inline const std::vector<std::string> &dataDirs() const noexcept
-    {
-        return m_dataDirs;
-    }
-};
-
-} // !irccd
-
-#endif // !XDG_HPP