# HG changeset patch # User David Demelier # Date 1463676432 -7200 # Node ID f94e42e8bf1ce66175594ae187237a25a70b79dd # Parent ff26bd33a45d9e4cb64b039e88f91c4d161dc446 Irccd: brand new format section, #410 diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/config.cpp --- a/lib/irccd/config.cpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/config.cpp Thu May 19 18:47:12 2016 +0200 @@ -489,6 +489,23 @@ 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")); @@ -622,6 +639,7 @@ } irccd.pluginService().configure(option.key(), findPluginConfig(option.key())); + irccd.pluginService().setFormats(option.key(), findPluginFormats(option.key())); irccd.pluginService().load(option.key(), option.value()); } } diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/config.hpp --- a/lib/irccd/config.hpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/config.hpp Thu May 19 18:47:12 2016 +0200 @@ -95,6 +95,14 @@ 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 + */ + PluginFormats findPluginFormats(const std::string &name) const; + + /** * Get the path to the pidfile. * * \return the path or empty if not defined diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/mod-plugin.cpp --- a/lib/irccd/mod-plugin.cpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/mod-plugin.cpp Thu May 19 18:47:12 2016 +0200 @@ -193,6 +193,8 @@ duk::push(plugin.context(), functions); duk::push(plugin.context(), duk::Object{}); duk::putProperty(plugin.context(), -2, "config"); + duk::push(plugin.context(), duk::Object{}); + duk::putProperty(plugin.context(), -2, "format"); duk::putProperty(plugin.context(), -2, "Plugin"); duk::pop(plugin.context()); } diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/plugin-js.cpp --- a/lib/irccd/plugin-js.cpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/plugin-js.cpp Thu May 19 18:47:12 2016 +0200 @@ -38,11 +38,11 @@ { duk::getGlobal(m_context, name); - if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED) { - /* Function not defined, remove the undefined value and all arguments */ + if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED) + // Function not defined, remove the undefined value and all arguments. duk::pop(m_context, nargs + 1); - } else { - /* Call the function and discard the result */ + else { + // Call the function and discard the result. duk::insert(m_context, -nargs - 1); if (duk::pcall(m_context, nargs) != 0) { @@ -51,12 +51,17 @@ duk::pop(m_context); throw error; - } else { + } else duk::pop(m_context); - } } } +void JsPlugin::putModules(Irccd &irccd) +{ + for (const auto &module : irccd.moduleService().modules()) + module->load(irccd, *this); +} + void JsPlugin::putVars() { duk::StackAssert sa(m_context); @@ -84,9 +89,8 @@ } // Use the system as default. - if (!found) { + if (!found) foundpath = path::clean(path::get(type, path::OwnerSystem) + append); - } duk::getGlobal(m_context, "Irccd"); duk::getProperty(m_context, -1, "Plugin"); @@ -98,26 +102,35 @@ { duk::StackAssert sa(m_context); - // TODO: override dataPath, configPath, cachePath + // TODO: override dataPath, configPath, cachePath. + // TODO: verify more that these values still exist. - /* Store plugin configuration into Irccd.Plugin.config */ + // Store plugin configuration into Irccd.Plugin.config. duk::getGlobal(m_context, "Irccd"); duk::getProperty(m_context, -1, "Plugin"); duk::getProperty(m_context, -1, "config"); - if (duk::type(m_context, -1) != DUK_TYPE_OBJECT) { - duk::pop(m_context); - duk::push(m_context, duk::Object{}); - } - - for (const auto &pair : config) { + for (const auto &pair : config) duk::putProperty(m_context, -1, pair.first, pair.second); - } duk::putProperty(m_context, -2, "config"); duk::pop(m_context, 2); } +void JsPlugin::putFormats() +{ + duk::StackAssert sa(m_context); + + duk::getGlobal(m_context, "Irccd"); + duk::getProperty(m_context, -1, "Plugin"); + duk::getProperty(m_context, -1, "format"); + + for (const auto &pair : formats()) + duk::putProperty(m_context, -1, pair.first, pair.second); + + duk::pop(m_context, 3); +} + JsPlugin::JsPlugin(std::string name, std::string path, const PluginConfig &config) : Plugin(name, path, config) { @@ -215,13 +228,6 @@ call("onKick", 5); } -void JsPlugin::putModules(Irccd &irccd) -{ - for (const auto &module : irccd.moduleService().modules()) { - module->load(irccd, *this); - } -} - void JsPlugin::onLoad(Irccd &irccd) { duk::StackAssert sa(m_context); @@ -248,16 +254,16 @@ putPath("configPath", "plugin/" + name(), path::PathConfig); putPath("cachePath", "plugin/" + name(), path::PathCache); - /* Try to load the file (does not call onLoad yet) */ - if (duk::pevalFile(m_context, path()) != 0) { + // Try to load the file (does not call onLoad yet). + if (duk::pevalFile(m_context, path()) != 0) throw duk::error(m_context, -1); - } duk::pop(m_context); putConfig(config()); + putFormats(); - /* Read metadata */ + // Read metadata . duk::getGlobal(m_context, "info"); if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) { @@ -411,9 +417,8 @@ call("onUnload"); - for (const auto &module : irccd.moduleService().modules()) { + for (const auto &module : irccd.moduleService().modules()) module->unload(irccd, *this); - } } void JsPlugin::onWhois(Irccd &, const std::shared_ptr &server, const ServerWhois &whois) diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/plugin-js.hpp --- a/lib/irccd/plugin-js.hpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/plugin-js.hpp Thu May 19 18:47:12 2016 +0200 @@ -50,6 +50,7 @@ void putVars(); void putPath(const std::string &varname, const std::string &append, path::Path type); void putConfig(const PluginConfig &config); + void putFormats(); public: /** @@ -61,11 +62,6 @@ */ JsPlugin(std::string name, std::string path, const PluginConfig &config = PluginConfig()); - ~JsPlugin() - { - puts("~JsPlugin"); - } - /** * Access the Duktape context. * diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/plugin.hpp --- a/lib/irccd/plugin.hpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/plugin.hpp Thu May 19 18:47:12 2016 +0200 @@ -38,11 +38,16 @@ class ServerWhois; /** - * Configuration map extract from config file. + * \brief Configuration map extract from config file. */ using PluginConfig = std::unordered_map; /** + * \brief Formats for plugins. + */ +using PluginFormats = std::unordered_map; + +/** * \class Plugin * \brief JavaScript plugin * @@ -61,7 +66,9 @@ std::string m_summary{"unknown"}; std::string m_version{"unknown"}; + // Configuration and formats. PluginConfig m_config; + PluginFormats m_formats; public: /** @@ -206,6 +213,36 @@ } /** + * Access the plugin formats. + * + * \return the format + */ + inline const PluginFormats &formats() const noexcept + { + return m_formats; + } + + /** + * Overloaded function. + * + * \return the formats + */ + inline PluginFormats &formats() noexcept + { + return m_formats; + } + + /** + * Set the formats. + * + * \param formats the formats + */ + inline void setFormats(PluginFormats formats) noexcept + { + m_formats = std::move(formats); + } + + /** * On channel message. This event will call onMessage or * onCommand if the messages starts with the command character * plus the plugin name. diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/service-plugin.cpp --- a/lib/irccd/service-plugin.cpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/service-plugin.cpp Thu May 19 18:47:12 2016 +0200 @@ -1,181 +1,197 @@ -/* - * service-plugin.cpp -- manage plugins - * - * Copyright (c) 2013-2016 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 "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 { - -namespace { - -std::shared_ptr find(std::unordered_map &configs, std::string name) -{ - PluginConfig config = configs[name]; - - for (const auto &path : path::list(path::PathPlugins)) { - std::string jspath = path + name + ".js"; - std::string dynlibpath = path + name + DYNLIB_SUFFIX; - - if (fs::isReadable(jspath)) - return std::make_shared(std::move(name), std::move(jspath), std::move(config)); - if (fs::isReadable(dynlibpath)) - return std::make_shared(std::move(name), std::move(dynlibpath)); - } - - throw std::runtime_error("no suitable plugin found"); -} - -std::shared_ptr open(std::string name, std::string path) -{ - std::regex regex(".*(\\..*)$"); - std::smatch match; - std::shared_ptr plugin; - - if (std::regex_match(path, match, regex)) { - std::string extension = match[1]; - - if (extension == DYNLIB_SUFFIX) - plugin = std::make_shared(name, path); - else - plugin = std::make_shared(name, path); - } else - throw std::runtime_error("could not deduce plugin type from {}"_format(path)); - - return plugin; -} - -} // !namespace - -PluginService::PluginService(Irccd &irccd) noexcept - : m_irccd(irccd) -{ -} - -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 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 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) -{ - m_plugins.push_back(std::move(plugin)); -} - -void PluginService::configure(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::load(std::string name, std::string path) -{ - auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { - return plugin->name() == name; - }); - - if (it != m_plugins.end()) - return; - - try { - std::shared_ptr plugin; - - if (path.empty()) - plugin = find(m_config, std::move(name)); - else - plugin = open(std::move(name), std::move(path)); - - plugin->onLoad(m_irccd); - add(std::move(plugin)); - } catch (const std::exception &ex) { - log::warning("plugin {}: {}"_format(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 +/* + * service-plugin.cpp -- manage plugins + * + * Copyright (c) 2013-2016 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 "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 { + +namespace { + +std::shared_ptr find(std::unordered_map &configs, std::string name) +{ + PluginConfig config = configs[name]; + + for (const auto &path : path::list(path::PathPlugins)) { + std::string jspath = path + name + ".js"; + std::string dynlibpath = path + name + DYNLIB_SUFFIX; + + if (fs::isReadable(jspath)) + return std::make_shared(std::move(name), std::move(jspath), std::move(config)); + if (fs::isReadable(dynlibpath)) + return std::make_shared(std::move(name), std::move(dynlibpath)); + } + + throw std::runtime_error("no suitable plugin found"); +} + +std::shared_ptr open(std::string name, std::string path) +{ + std::regex regex(".*(\\..*)$"); + std::smatch match; + std::shared_ptr plugin; + + if (std::regex_match(path, match, regex)) { + std::string extension = match[1]; + + if (extension == DYNLIB_SUFFIX) + plugin = std::make_shared(name, path); + else + plugin = std::make_shared(name, path); + } else + throw std::runtime_error("could not deduce plugin type from {}"_format(path)); + + return plugin; +} + +} // !namespace + +PluginService::PluginService(Irccd &irccd) noexcept + : m_irccd(irccd) +{ +} + +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 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 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) +{ + m_plugins.push_back(std::move(plugin)); +} + +void PluginService::configure(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(); +} + +void PluginService::load(std::string name, std::string path) +{ + auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { + return plugin->name() == name; + }); + + if (it != m_plugins.end()) + return; + + try { + std::shared_ptr plugin; + + if (path.empty()) + plugin = find(m_config, std::move(name)); + else + plugin = open(std::move(name), std::move(path)); + + plugin->setFormats(m_formats[plugin->name()]); + plugin->onLoad(m_irccd); + add(std::move(plugin)); + } catch (const std::exception &ex) { + log::warning("plugin {}: {}"_format(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 diff -r ff26bd33a45d -r f94e42e8bf1c lib/irccd/service-plugin.hpp --- a/lib/irccd/service-plugin.hpp Thu May 19 13:00:00 2016 +0200 +++ b/lib/irccd/service-plugin.hpp Thu May 19 18:47:12 2016 +0200 @@ -42,6 +42,7 @@ Irccd &m_irccd; std::vector> m_plugins; std::unordered_map m_config; + std::unordered_map m_formats; public: /** @@ -119,6 +120,22 @@ PluginConfig config(const std::string &name) const; /** + * Add formatting for a plugin. + * + * \param name the plugin name + * \param formats the formats + */ + void setFormats(const std::string &name, PluginFormats formats); + + /** + * Get formats for a plugin. + * + * \param name the plugin name + * \return the formats + */ + PluginFormats formats(const std::string &name) const; + + /** * Convenient wrapper that loads a plugin, call onLoad and add it to the registry. * * Any errors are printed using logger.