# HG changeset patch # User David Demelier # Date 1521119091 -3600 # Node ID 7632483ca971cdb61f9306f4664475279582dc0d # Parent 152d20dc0e74fb59d118c49b1099dd427cc9d214 Irccd: implement server_util, #771 diff -r 152d20dc0e74 -r 7632483ca971 libirccd-js/irccd/js/server_jsapi.cpp --- a/libirccd-js/irccd/js/server_jsapi.cpp Thu Mar 15 12:56:26 2018 +0100 +++ b/libirccd-js/irccd/js/server_jsapi.cpp Thu Mar 15 14:04:51 2018 +0100 @@ -21,6 +21,7 @@ #include #include +#include #include @@ -372,7 +373,7 @@ try { auto json = duk_json_encode(ctx, 0); - auto s = server_service::from_json(dukx_get_irccd(ctx).service(), nlohmann::json::parse(json)); + auto s = server_util::from_json(dukx_get_irccd(ctx).service(), nlohmann::json::parse(json)); duk_push_this(ctx); duk_push_pointer(ctx, new std::shared_ptr(std::move(s))); diff -r 152d20dc0e74 -r 7632483ca971 libirccd/CMakeLists.txt --- a/libirccd/CMakeLists.txt Thu Mar 15 12:56:26 2018 +0100 +++ b/libirccd/CMakeLists.txt Thu Mar 15 14:04:51 2018 +0100 @@ -62,6 +62,7 @@ ${libirccd_SOURCE_DIR}/irccd/daemon/plugin.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/rule.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/server.hpp + ${libirccd_SOURCE_DIR}/irccd/daemon/server_util.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/plugin_service.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/rule_service.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/server_service.hpp @@ -107,6 +108,7 @@ ${libirccd_SOURCE_DIR}/irccd/daemon/plugin.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/rule.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/server.cpp + ${libirccd_SOURCE_DIR}/irccd/daemon/server_util.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/plugin_service.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/rule_service.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/service/server_service.cpp diff -r 152d20dc0e74 -r 7632483ca971 libirccd/irccd/daemon/command/server_connect_command.cpp --- a/libirccd/irccd/daemon/command/server_connect_command.cpp Thu Mar 15 12:56:26 2018 +0100 +++ b/libirccd/irccd/daemon/command/server_connect_command.cpp Thu Mar 15 14:04:51 2018 +0100 @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -32,7 +33,7 @@ void server_connect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = server_service::from_json(irccd.service(), args); + auto server = server_util::from_json(irccd.service(), args); if (irccd.servers().has(server->name())) throw server_error(server_error::error::already_exists, server->name()); diff -r 152d20dc0e74 -r 7632483ca971 libirccd/irccd/daemon/server_util.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/daemon/server_util.cpp Thu Mar 15 14:04:51 2018 +0100 @@ -0,0 +1,254 @@ +/* + * server_util.cpp -- server utilities + * + * Copyright (c) 2013-2018 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "server_util.hpp" + +namespace irccd { + +namespace server_util { + +namespace { + +template +T to_int(const std::string& value, const std::string& name, server_error::error errc) +{ + try { + return string_util::to_int(value); + } catch (...) { + throw server_error(errc, name); + } +} + +template +T to_uint(const std::string& value, const std::string& name, server_error::error errc) +{ + try { + return string_util::to_uint(value); + } catch (...) { + throw server_error(errc, name); + } +} + +template +T to_uint(const nlohmann::json& value, const std::string& name, server_error::error errc) +{ + if (!value.is_number()) + throw server_error(errc, name); + + auto n = value.get(); + + if (n > std::numeric_limits::max()) + throw server_error(errc, name); + + return static_cast(n); +} + +std::string to_id(const ini::section& sc) +{ + auto id = sc.get("name"); + + if (!string_util::is_identifier(id.value())) + throw server_error(server_error::invalid_identifier, ""); + + return id.value(); +} + +std::string to_id(const nlohmann::json& object) +{ + auto id = json_util::get_string(object, "name"); + + if (!string_util::is_identifier(id)) + throw server_error(server_error::invalid_identifier, ""); + + return id; +} + +std::string to_host(const ini::section& sc, const std::string& name) +{ + auto value = sc.get("host"); + + if (value.empty()) + throw server_error(server_error::invalid_hostname, name); + + return value.value(); +} + +std::string to_host(const nlohmann::json& object, const std::string& name) +{ + auto value = json_util::get_string(object, "host"); + + if (value.empty()) + throw server_error(server_error::invalid_hostname, name); + + return value; +} + +void load_identity(server& server, const config& cfg, const std::string& identity) +{ + auto sc = std::find_if(cfg.doc().begin(), cfg.doc().end(), [&] (const auto& sc) { + if (sc.key() != "identity") + return false; + + auto name = sc.find("name"); + + return name != sc.end() && name->value() == identity; + }); + + if (sc == cfg.doc().end()) + return; + + ini::section::const_iterator it; + + if ((it = sc->find("username")) != sc->end()) + server.set_username(it->value()); + if ((it = sc->find("realname")) != sc->end()) + server.set_realname(it->value()); + if ((it = sc->find("nickname")) != sc->end()) + server.set_nickname(it->value()); + if ((it = sc->find("ctcp-version")) != sc->end()) + server.set_ctcp_version(it->value()); +} + +} // !namespace + +std::shared_ptr from_json(boost::asio::io_service& service, const nlohmann::json& object) +{ + // TODO: move this function in server_service. + auto sv = std::make_shared(service, to_id(object)); + + // Mandatory fields. + sv->set_host(to_host(object, sv->name())); + + // Optional fields. + if (object.count("port")) + sv->set_port(to_uint(object["port"], sv->name(), server_error::invalid_port)); + sv->set_password(json_util::get_string(object, "password")); + sv->set_nickname(json_util::get_string(object, "nickname", sv->nickname())); + sv->set_realname(json_util::get_string(object, "realname", sv->realname())); + sv->set_username(json_util::get_string(object, "username", sv->username())); + sv->set_ctcp_version(json_util::get_string(object, "ctcpVersion", sv->ctcp_version())); + sv->set_command_char(json_util::get_string(object, "commandChar", sv->command_char())); + + if (json_util::get_bool(object, "ipv6")) + sv->set_flags(sv->flags() | server::ipv6); + if (json_util::get_bool(object, "sslVerify")) + sv->set_flags(sv->flags() | server::ssl_verify); + if (json_util::get_bool(object, "autoRejoin")) + sv->set_flags(sv->flags() | server::auto_rejoin); + if (json_util::get_bool(object, "joinInvite")) + sv->set_flags(sv->flags() | server::join_invite); + + if (json_util::get_bool(object, "ssl")) +#if defined(HAVE_SSL) + sv->set_flags(sv->flags() | server::ssl); +#else + throw server_error(server_error::ssl_disabled, sv->name()); +#endif + + return sv; +} + +std::shared_ptr from_config(boost::asio::io_service& service, + const config& cfg, + const ini::section& sc) +{ + assert(sc.key() == "server"); + + auto sv = std::make_shared(service, to_id(sc)); + + // Mandatory fields. + sv->set_host(to_host(sc, sv->name())); + + // Optional fields. + ini::section::const_iterator it; + + if ((it = sc.find("password")) != sc.end()) + sv->set_password(it->value()); + + // Optional flags + if ((it = sc.find("ipv6")) != sc.end() && string_util::is_boolean(it->value())) + sv->set_flags(sv->flags() | server::ipv6); + + if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) { +#if defined(HAVE_SSL) + sv->set_flags(sv->flags() | server::ssl); +#else + throw server_error(server_error::ssl_disabled, sv->name()); +#endif + } + + if ((it = sc.find("ssl-verify")) != sc.end() && string_util::is_boolean(it->value())) + sv->set_flags(sv->flags() | server::ssl_verify); + + // Optional identity + if ((it = sc.find("identity")) != sc.end()) + load_identity(*sv, cfg, it->value()); + + // Options + if ((it = sc.find("auto-rejoin")) != sc.end() && string_util::is_boolean(it->value())) + sv->set_flags(sv->flags() | server::auto_rejoin); + if ((it = sc.find("join-invite")) != sc.end() && string_util::is_boolean(it->value())) + sv->set_flags(sv->flags() | server::join_invite); + + // Channels + if ((it = sc.find("channels")) != sc.end()) { + for (const auto& 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; + + sv->join(channel.name, channel.password); + } + } + if ((it = sc.find("command-char")) != sc.end()) + sv->set_command_char(it->value()); + + // Reconnect and ping timeout + if ((it = sc.find("port")) != sc.end()) + sv->set_port(to_uint(it->value(), + sv->name(), server_error::invalid_port)); + + if ((it = sc.find("reconnect-tries")) != sc.end()) + sv->set_reconnect_tries(to_int(it->value(), + sv->name(), server_error::invalid_reconnect_tries)); + + if ((it = sc.find("reconnect-timeout")) != sc.end()) + sv->set_reconnect_delay(to_uint(it->value(), + sv->name(), server_error::invalid_reconnect_timeout)); + + if ((it = sc.find("ping-timeout")) != sc.end()) + sv->set_ping_timeout(to_uint(it->value(), + sv->name(), server_error::invalid_ping_timeout)); + + return sv; +} + +} // !server_util + +} // !irccd diff -r 152d20dc0e74 -r 7632483ca971 libirccd/irccd/daemon/server_util.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd/irccd/daemon/server_util.hpp Thu Mar 15 14:04:51 2018 +0100 @@ -0,0 +1,78 @@ +/* + * server_util.hpp -- server utilities + * + * Copyright (c) 2013-2018 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_DAEMON_SERVER_UTIL_HPP +#define IRCCD_DAEMON_SERVER_UTIL_HPP + +/** + * \file server_util.hpp + * \brief Server utilities. + */ + +#include + +#include + +#include + +#include + +namespace irccd { + +namespace ini { + +class section; + +} // !ini + +class config; + +/** + * \brief Server utilities. + */ +namespace server_util { + +/** + * Convert a JSON object to a server. + * + * \param service the io service + * \param object the object + * \return the server + * \throw server_error on errors + */ +std::shared_ptr from_json(boost::asio::io_service& service, + const nlohmann::json& object); + +/** + * Convert a INI section to a server. + * + * \param service the io service + * \param cfg the whole configuration + * \param sc the server section + * \return the server + * \throw server_error on errors + */ +std::shared_ptr from_config(boost::asio::io_service& service, + const config& cfg, + const ini::section& sc); + +} // !server_util + +} // !irccd + +#endif // !IRCCD_DAEMON_SERVER_UTIL_HPP diff -r 152d20dc0e74 -r 7632483ca971 libirccd/irccd/daemon/service/server_service.cpp --- a/libirccd/irccd/daemon/service/server_service.cpp Thu Mar 15 12:56:26 2018 +0100 +++ b/libirccd/irccd/daemon/service/server_service.cpp Thu Mar 15 14:04:51 2018 +0100 @@ -1,5 +1,5 @@ /* - * server_service.hpp -- server service + * server_service.cpp -- server service * * Copyright (c) 2013-2018 David Demelier * @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -59,187 +60,6 @@ } } -template -T to_int(const std::string& value, const std::string& name, server_error::error errc) -{ - try { - return string_util::to_int(value); - } catch (...) { - throw server_error(errc, name); - } -} - -template -T to_uint(const std::string& value, const std::string& name, server_error::error errc) -{ - try { - return string_util::to_uint(value); - } catch (...) { - throw server_error(errc, name); - } -} - -template -T to_uint(const nlohmann::json& value, const std::string& name, server_error::error errc) -{ - if (!value.is_number()) - throw server_error(errc, name); - - auto n = value.get(); - - if (n > std::numeric_limits::max()) - throw server_error(errc, name); - - return static_cast(n); -} - -std::string to_id(const ini::section& sc) -{ - auto id = sc.get("name"); - - if (!string_util::is_identifier(id.value())) - throw server_error(server_error::invalid_identifier, ""); - - return id.value(); -} - -std::string to_id(const nlohmann::json& object) -{ - auto id = json_util::get_string(object, "name"); - - if (!string_util::is_identifier(id)) - throw server_error(server_error::invalid_identifier, ""); - - return id; -} - -std::string to_host(const ini::section& sc, const std::string& name) -{ - auto value = sc.get("host"); - - if (value.empty()) - throw server_error(server_error::invalid_hostname, name); - - return value.value(); -} - -std::string to_host(const nlohmann::json& object, const std::string& name) -{ - auto value = json_util::get_string(object, "host"); - - if (value.empty()) - throw server_error(server_error::invalid_hostname, name); - - return value; -} - -void load_server_identity(std::shared_ptr& server, - const config& cfg, - const std::string& identity) -{ - auto sc = std::find_if(cfg.doc().begin(), cfg.doc().end(), [&] (const auto& sc) { - if (sc.key() != "identity") - return false; - - auto name = sc.find("name"); - - return name != sc.end() && name->value() == identity; - }); - - if (sc == cfg.doc().end()) - return; - - ini::section::const_iterator it; - - if ((it = sc->find("username")) != sc->end()) - server->set_username(it->value()); - if ((it = sc->find("realname")) != sc->end()) - server->set_realname(it->value()); - if ((it = sc->find("nickname")) != sc->end()) - server->set_nickname(it->value()); - if ((it = sc->find("ctcp-version")) != sc->end()) - server->set_ctcp_version(it->value()); -} - -std::shared_ptr load_server(boost::asio::io_service& service, - const config& cfg, - const ini::section& sc) -{ - assert(sc.key() == "server"); - - auto sv = std::make_shared(service, to_id(sc)); - - // Mandatory fields. - sv->set_host(to_host(sc, sv->name())); - - // Optional fields. - ini::section::const_iterator it; - - if ((it = sc.find("password")) != sc.end()) - sv->set_password(it->value()); - - // Optional flags - if ((it = sc.find("ipv6")) != sc.end() && string_util::is_boolean(it->value())) - sv->set_flags(sv->flags() | server::ipv6); - - if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) { -#if defined(HAVE_SSL) - sv->set_flags(sv->flags() | server::ssl); -#else - throw server_error(server_error::ssl_disabled, sv->name()); -#endif - } - - if ((it = sc.find("ssl-verify")) != sc.end() && string_util::is_boolean(it->value())) - sv->set_flags(sv->flags() | server::ssl_verify); - - // Optional identity - if ((it = sc.find("identity")) != sc.end()) - load_server_identity(sv, cfg, it->value()); - - // Options - if ((it = sc.find("auto-rejoin")) != sc.end() && string_util::is_boolean(it->value())) - sv->set_flags(sv->flags() | server::auto_rejoin); - if ((it = sc.find("join-invite")) != sc.end() && string_util::is_boolean(it->value())) - sv->set_flags(sv->flags() | server::join_invite); - - // Channels - if ((it = sc.find("channels")) != sc.end()) { - for (const auto& 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; - - sv->join(channel.name, channel.password); - } - } - if ((it = sc.find("command-char")) != sc.end()) - sv->set_command_char(it->value()); - - // Reconnect and ping timeout - if ((it = sc.find("port")) != sc.end()) - sv->set_port(to_uint(it->value(), - sv->name(), server_error::invalid_port)); - - if ((it = sc.find("reconnect-tries")) != sc.end()) - sv->set_reconnect_tries(to_int(it->value(), - sv->name(), server_error::invalid_reconnect_tries)); - - if ((it = sc.find("reconnect-timeout")) != sc.end()) - sv->set_reconnect_delay(to_uint(it->value(), - sv->name(), server_error::invalid_reconnect_timeout)); - - if ((it = sc.find("ping-timeout")) != sc.end()) - sv->set_ping_timeout(to_uint(it->value(), - sv->name(), server_error::invalid_ping_timeout)); - - return sv; -} - } // !namespace void server_service::handle_connect(const connect_event& ev) @@ -603,43 +423,6 @@ ); } -std::shared_ptr server_service::from_json(boost::asio::io_service& service, const nlohmann::json& object) -{ - // TODO: move this function in server_service. - auto sv = std::make_shared(service, to_id(object)); - - // Mandatory fields. - sv->set_host(to_host(object, sv->name())); - - // Optional fields. - if (object.count("port")) - sv->set_port(to_uint(object["port"], sv->name(), server_error::invalid_port)); - sv->set_password(json_util::get_string(object, "password")); - sv->set_nickname(json_util::get_string(object, "nickname", sv->nickname())); - sv->set_realname(json_util::get_string(object, "realname", sv->realname())); - sv->set_username(json_util::get_string(object, "username", sv->username())); - sv->set_ctcp_version(json_util::get_string(object, "ctcpVersion", sv->ctcp_version())); - sv->set_command_char(json_util::get_string(object, "commandChar", sv->command_char())); - - if (json_util::get_bool(object, "ipv6")) - sv->set_flags(sv->flags() | server::ipv6); - if (json_util::get_bool(object, "sslVerify")) - sv->set_flags(sv->flags() | server::ssl_verify); - if (json_util::get_bool(object, "autoRejoin")) - sv->set_flags(sv->flags() | server::auto_rejoin); - if (json_util::get_bool(object, "joinInvite")) - sv->set_flags(sv->flags() | server::join_invite); - - if (json_util::get_bool(object, "ssl")) -#if defined(HAVE_SSL) - sv->set_flags(sv->flags() | server::ssl); -#else - throw server_error(server_error::ssl_disabled, sv->name()); -#endif - - return sv; -} - server_service::server_service(irccd &irccd) : irccd_(irccd) { @@ -734,7 +517,7 @@ continue; try { - add(load_server(irccd_.service(), cfg, section)); + add(server_util::from_config(irccd_.service(), cfg, section)); } catch (const std::exception& ex) { irccd_.log().warning() << "server " << section.get("name").value() << ": " << ex.what() << std::endl; diff -r 152d20dc0e74 -r 7632483ca971 libirccd/irccd/daemon/service/server_service.hpp --- a/libirccd/irccd/daemon/service/server_service.hpp Thu Mar 15 12:56:26 2018 +0100 +++ b/libirccd/irccd/daemon/service/server_service.hpp Thu Mar 15 14:04:51 2018 +0100 @@ -61,18 +61,6 @@ public: /** - * Convert a JSON object as a server. - * - * Used in JavaScript API and transport commands. - * - * \param service the io service - * \param object the object - * \return the server - * \throw std::exception on failures - */ - static std::shared_ptr from_json(boost::asio::io_service& service, const nlohmann::json& object); - - /** * Create the server service. */ server_service(irccd& instance);