Mercurial > irccd
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 §ion, 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 §ion : 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 §ion : 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 §ion : 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 §ion : 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 ®ex) -{ - 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 ¶ms) -{ - std::ostringstream oss; - -#if defined(HAVE_STD_PUT_TIME) - oss << std::put_time(std::localtime(¶ms.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(¶ms.time)); - - oss << buffer; -#endif - - return oss.str(); -} - -std::string substituteKeywords(const std::string &content, const Substitution ¶ms) -{ - 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 ¶ms) -{ - 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 ¶ms) -{ - /* - * 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 ¶ms = {}); - -/** - * 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