changeset 520:defacef00c82

Irccd: load plugin configuration dynamically, closes #724
author David Demelier <markand@malikania.fr>
date Tue, 31 Oct 2017 10:48:06 +0100
parents 63faa8087c46
children e03521cf207b
files libcommon/irccd/ini.cpp libcommon/irccd/ini.hpp libirccd/irccd/config.cpp libirccd/irccd/config.hpp libirccd/irccd/service.cpp libirccd/irccd/service.hpp
diffstat 6 files changed, 185 insertions(+), 356 deletions(-) [+]
line wrap: on
line diff
--- a/libcommon/irccd/ini.cpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libcommon/irccd/ini.cpp	Tue Oct 31 10:48:06 2017 +0100
@@ -128,23 +128,19 @@
     // Read section name.
     ++ it;
     while (it != end && *it != ']') {
-        if (*it == '\n') {
+        if (*it == '\n')
             throw exception(line, column, "section not terminated, missing ']'");
-        }
-        if (is_reserved(*it)) {
+        if (is_reserved(*it))
             throw exception(line, column, "section name expected after '[', got '" + std::string(1, *it) + "'");
-        }
 
         ++ column;
         value += *it++;
     }
 
-    if (it == end) {
+    if (it == end)
         throw exception(line, column, "section name expected after '[', got <EOF>");
-    }
-    if (value.empty()) {
+    if (value.empty())
         throw exception(line, column, "empty section name");
-    }
 
     // Remove ']'.
     ++ it;
@@ -172,9 +168,8 @@
         value += *it++;
     }
 
-    if (it == end) {
+    if (it == end)
         throw exception(line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>");
-    }
 
     // Remove quote.
     ++ it;
@@ -211,9 +206,8 @@
         include += *it++;
     }
 
-    if (include != "include") {
+    if (include != "include")
         throw exception(line, column, "expected include after '@' token");
-    }
 
     list.push_back({ token::include, line, save });
 }
@@ -235,9 +229,8 @@
         switch (it->type()) {
         case token::comma:
             // Previous must be a word.
-            if (it[-1].type() != token::word && it[-1].type() != token::quoted_word) {
+            if (it[-1].type() != token::word && it[-1].type() != token::quoted_word)
                 throw exception(it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'");
-            }
 
             ++ it;
             break;
@@ -251,9 +244,8 @@
         }
     }
 
-    if (it == end) {
+    if (it == end)
         throw exception(save->line(), save->column(), "unterminated list construct");
-    }
 
     // Remove ).
     ++ it;
@@ -265,20 +257,17 @@
     token_iterator save(it);
 
     // No '=' or something else?
-    if (++it == end) {
+    if (++it == end)
         throw exception(save->line(), save->column(), "expected '=' assignment, got <EOF>");
-    }
-    if (it->type() != token::assign) {
+    if (it->type() != token::assign)
         throw exception(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::quoted_word) {
+        if (it->type() == token::word || it->type() == token::quoted_word)
             parse_option_value_simple(option, it);
-        } else if (it->type() == token::list_begin) {
+        else if (it->type() == token::list_begin)
             parse_option_value_list(option, it, end);
-        }
     }
 
     sc.push_back(std::move(option));
@@ -288,12 +277,10 @@
 {
     token_iterator save(it);
 
-    if (++it == end) {
+    if (++it == end)
         throw exception(save->line(), save->column(), "expected file name after '@include' statement, got <EOF>");
-    }
-    if (it->type() != token::word && it->type() != token::quoted_word) {
+    if (it->type() != token::word && it->type() != token::quoted_word)
         throw exception(it->line(), it->column(), "expected file name after '@include' statement, got " + it->value());
-    }
 
     std::string value = (it++)->value();
     std::string file;
@@ -304,13 +291,11 @@
 #else
         file = path + "/" + value;
 #endif
-    } else {
+    } else
         file = value;
-    }
 
-    for (const auto& sc : read_file(file)) {
+    for (const auto& sc : read_file(file))
         doc.push_back(sc);
-    }
 }
 
 void parse_section(document& doc, token_iterator& it, token_iterator end)
@@ -322,9 +307,8 @@
 
     // Read until next section.
     while (it != end && it->type() != token::section) {
-        if (it->type() != token::word) {
+        if (it->type() != token::word)
             throw exception(it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition");
-        }
 
         parse_option(sc, it, end);
     }
@@ -341,25 +325,24 @@
     int column = 0;
 
     while (it != end) {
-        if (*it == '\n') {
+        if (*it == '\n')
             analyse_line(line, column, it);
-        } else if (*it == '#') {
+        else if (*it == '#')
             analyse_comment(column, it, end);
-        } else if (*it == '[') {
+        else if (*it == '[')
             analyse_section(list, line, column, it, end);
-        } else if (*it == '=') {
+        else if (*it == '=')
             analyse_assign(list, line, column, it);
-        } else if (is_space(*it)) {
+        else if (is_space(*it))
             analyse_spaces(column, it, end);
-        } else if (*it == '@') {
+        else if (*it == '@')
             analyse_include(list, line, column, it, end);
-        } else if (is_quote(*it)) {
+        else if (is_quote(*it))
             analyse_quoted_word(list, line, column, it, end);
-        } else if (is_list(*it)) {
+        else if (is_list(*it))
             analyse_list(list, line, column, it);
-        } else {
+        else
             analyse_word(list, line, column, it, end);
-        }
     }
 
     return list;
@@ -398,17 +381,15 @@
     auto parent = filename;
     auto pos = parent.find_last_of("/\\");
 
-    if (pos != std::string::npos) {
+    if (pos != std::string::npos)
         parent.erase(pos);
-    } else {
+    else
         parent = ".";
-    }
 
     std::ifstream input(filename);
 
-    if (!input) {
+    if (!input)
         throw exception(0, 0, std::strerror(errno));
-    }
 
     return parse(analyse(input), parent);
 }
--- a/libcommon/irccd/ini.hpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libcommon/irccd/ini.hpp	Tue Oct 31 10:48:06 2017 +0100
@@ -16,20 +16,26 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef INI_HPP
-#define INI_HPP
+#ifndef IRCCD_INI_HPP
+#define IRCCD_INI_HPP
 
 /**
  * \file ini.hpp
  * \brief Extended .ini file parser.
  * \author David Demelier <markand@malikania.fr>
- * \version 1.0.0
+ * \version 2.0.0
  */
 
 /**
  * \page Ini Ini
  * \brief Extended .ini file parser.
  *
+ * ## Export macros
+ *
+ * You must define `INI_DLL` globally and `INI_BUILDING_DLL` when compiling the
+ * library if you want a DLL, alternatively you can provide your own
+ * `` macro instead.
+ *
  *   - \subpage ini-syntax
  */
 
@@ -42,10 +48,10 @@
  *   - 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
+ *   - lists can not include trailing commas,
+ *   - include statements must always live at the beginning of files
  *     (in no sections),
- *   - comments starts with # until the end of line,
+ *   - comments start with # until the end of line,
  *   - options with spaces **must** use quotes.
  *
  * # Basic file
@@ -70,7 +76,7 @@
  * value = "2"
  * ````
  *
- * The ini::document object will contains two ini::Section.
+ * The ini::document object will contains two ini::section.
  *
  * # Lists
  *
@@ -80,7 +86,7 @@
  * [section]
  * names = ( "x1", "x2" )
  *
- * # This is also allowed
+ * # This is also allowed.
  * biglist = (
  *   "abc",
  *   "def"
@@ -106,32 +112,6 @@
  * ````
  */
 
-/**
- * \cond INI_HIDDEN_SYMBOLS
- */
-
-#if !defined(IRCCD_EXPORT)
-#   if defined(INI_DLL)
-#       if defined(_WIN32)
-#           if defined(INI_BUILDING_DLL)
-#               define IRCCD_EXPORT __declspec(dllexport)
-#           else
-#               define IRCCD_EXPORT __declspec(dllimport)
-#           endif
-#       else
-#           define IRCCD_EXPORT
-#       endif
-#   else
-#       define IRCCD_EXPORT
-#   endif
-#endif
-
-/**
- * \endcond
- */
-
-#include <sysconfig.hpp>
-
 #include <algorithm>
 #include <cassert>
 #include <exception>
@@ -153,9 +133,9 @@
  */
 class exception : public std::exception {
 private:
-    int m_line;                 //!< line number
-    int m_column;               //!< line column
-    std::string m_message;      //!< exception message
+    int line_;
+    int column_;
+    std::string message_;
 
 public:
     /**
@@ -166,9 +146,9 @@
      * \param msg the message
      */
     inline exception(int line, int column, std::string msg) noexcept
-        : m_line(line)
-        , m_column(column)
-        , m_message(std::move(msg))
+        : line_(line)
+        , column_(column)
+        , message_(std::move(msg))
     {
     }
 
@@ -179,7 +159,7 @@
      */
     inline int line() const noexcept
     {
-        return m_line;
+        return line_;
     }
 
     /**
@@ -189,7 +169,7 @@
      */
     inline int column() const noexcept
     {
-        return m_column;
+        return column_;
     }
 
     /**
@@ -199,7 +179,7 @@
      */
     const char* what() const noexcept override
     {
-        return m_message.c_str();
+        return message_.c_str();
     }
 };
 
@@ -208,7 +188,7 @@
  *
  * This class can be used when you want to parse a .ini file yourself.
  *
- * \see analyze
+ * \see analyse
  */
 class token {
 public:
@@ -227,10 +207,10 @@
     };
 
 private:
-    type m_type;
-    int m_line;
-    int m_column;
-    std::string m_value;
+    type type_;
+    int line_;
+    int column_;
+    std::string value_;
 
 public:
     /**
@@ -242,30 +222,30 @@
      * \param value the value
      */
     token(type type, int line, int column, std::string value = "") noexcept
-        : m_type(type)
-        , m_line(line)
-        , m_column(column)
+        : type_(type)
+        , line_(line)
+        , column_(column)
     {
         switch (type) {
         case include:
-            m_value = "@include";
+            value_ = "@include";
             break;
         case section:
         case word:
         case quoted_word:
-            m_value = value;
+            value_ = value;
             break;
         case assign:
-            m_value = "=";
+            value_ = "=";
             break;
         case list_begin:
-            m_value = "(";
+            value_ = "(";
             break;
         case list_end:
-            m_value = ")";
+            value_ = ")";
             break;
         case comma:
-            m_value = ",";
+            value_ = ",";
             break;
         default:
             break;
@@ -279,7 +259,7 @@
      */
     inline type type() const noexcept
     {
-        return m_type;
+        return type_;
     }
 
     /**
@@ -289,7 +269,7 @@
      */
     inline int line() const noexcept
     {
-        return m_line;
+        return line_;
     }
 
     /**
@@ -299,7 +279,7 @@
      */
     inline int column() const noexcept
     {
-        return m_column;
+        return column_;
     }
 
     /**
@@ -310,7 +290,7 @@
      */
     inline const std::string& value() const noexcept
     {
-        return m_value;
+        return value_;
     }
 };
 
@@ -324,7 +304,7 @@
  */
 class option : public std::vector<std::string> {
 private:
-    std::string m_key;
+    std::string key_;
 
 public:
     /**
@@ -335,9 +315,9 @@
      */
     inline option(std::string key) noexcept
         : std::vector<std::string>()
-        , m_key(std::move(key))
+        , key_(std::move(key))
     {
-        assert(!m_key.empty());
+        assert(!key_.empty());
     }
 
     /**
@@ -348,9 +328,9 @@
      * \param value the value
      */
     inline option(std::string key, std::string value) noexcept
-        : m_key(std::move(key))
+        : key_(std::move(key))
     {
-        assert(!m_key.empty());
+        assert(!key_.empty());
 
         push_back(std::move(value));
     }
@@ -364,9 +344,9 @@
      */
     inline option(std::string key, std::vector<std::string> values) noexcept
         : std::vector<std::string>(std::move(values))
-        , m_key(std::move(key))
+        , key_(std::move(key))
     {
-        assert(!m_key.empty());
+        assert(!key_.empty());
     }
 
     /**
@@ -376,7 +356,7 @@
      */
     inline const std::string& key() const noexcept
     {
-        return m_key;
+        return key_;
     }
 
     /**
@@ -397,7 +377,7 @@
  */
 class section : public std::vector<option> {
 private:
-    std::string m_key;
+    std::string key_;
 
 public:
     /**
@@ -407,9 +387,9 @@
      * \param key the key
      */
     inline section(std::string key) noexcept
-        : m_key(std::move(key))
+        : key_(std::move(key))
     {
-        assert(!m_key.empty());
+        assert(!key_.empty());
     }
 
     /**
@@ -419,7 +399,7 @@
      */
     inline const std::string& key() const noexcept
     {
-        return m_key;
+        return key_;
     }
 
     /**
@@ -434,6 +414,22 @@
     }
 
     /**
+     * Find an option or return an empty one if not found.
+     *
+     * \param key the key
+     * \return the option or empty one if not found
+     */
+    inline option get(const std::string& key) const noexcept
+    {
+        auto it = find(key);
+
+        if (it == end())
+            return option(key);
+
+        return *it;
+    }
+
+    /**
      * Find an option by key and return an iterator.
      *
      * \param key the key
@@ -495,8 +491,8 @@
 
 /**
  * \brief Ini document description.
- * \see readFile
- * \see readString
+ * \see read_file
+ * \see read_string
  */
 class document : public std::vector<section> {
 public:
@@ -512,6 +508,22 @@
     }
 
     /**
+     * Find a section or return an empty one if not found.
+     *
+     * \param key the key
+     * \return the section or empty one if not found
+     */
+    inline section get(const std::string& key) const noexcept
+    {
+        auto it = find(key);
+
+        if (it == end())
+            return section(key);
+
+        return *it;
+    }
+
+    /**
      * Find a section by key and return an iterator.
      *
      * \param key the key
@@ -583,7 +595,7 @@
  * \return the list of tokens
  * \throws exception on errors
  */
-IRCCD_EXPORT tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
+ tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
 
 /**
  * Overloaded function for stream.
@@ -592,7 +604,7 @@
  * \return the list of tokens
  * \throws exception on errors
  */
-IRCCD_EXPORT tokens analyse(std::istream& stream);
+ tokens analyse(std::istream& stream);
 
 /**
  * Parse the produced tokens.
@@ -602,7 +614,7 @@
  * \return the document
  * \throw exception on errors
  */
-IRCCD_EXPORT document parse(const tokens& tokens, const std::string& path = ".");
+ document parse(const tokens& tokens, const std::string& path = ".");
 
 /**
  * Parse a file.
@@ -611,7 +623,7 @@
  * \return the document
  * \throw exception on errors
  */
-IRCCD_EXPORT document read_file(const std::string& filename);
+ document read_file(const std::string& filename);
 
 /**
  * Parse a string.
@@ -623,17 +635,17 @@
  * \return the document
  * \throw exception on exceptions
  */
-IRCCD_EXPORT document read_string(const std::string& buffer);
+ document read_string(const std::string& buffer);
 
 /**
  * Show all tokens and their description.
  *
  * \param tokens the tokens
  */
-IRCCD_EXPORT void dump(const tokens& tokens);
+ void dump(const tokens& tokens);
 
 } // !ini
 
 } // !irccd
 
-#endif // !INI_HPP
+#endif // !IRCCD_INI_HPP
--- a/libirccd/irccd/config.cpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libirccd/irccd/config.cpp	Tue Oct 31 10:48:06 2017 +0100
@@ -87,26 +87,6 @@
     return ito->value();
 }
 
-plugin_config load_plugin_config(const ini::section& sc)
-{
-    plugin_config config;
-
-    for (const auto& option : sc)
-        config.emplace(option.key(), option.value());
-
-    return config;
-}
-
-plugin_paths read_paths(const ini::section& sc)
-{
-    plugin_paths paths;
-
-    for (const auto& opt : sc)
-        paths.emplace(opt.key(), opt.value());
-
-    return paths;
-}
-
 std::unique_ptr<log::logger> load_log_file(const ini::section& sc)
 {
     /*
@@ -422,51 +402,6 @@
         server.set_ctcp_version(it->value());
 }
 
-plugin_config config::find_plugin_config(const std::string& name) const
-{
-    assert(util::is_identifier(name));
-
-    std::string fullname = std::string("plugin.") + name;
-
-    for (const auto& section : document_) {
-        if (section.key() != fullname)
-            continue;
-
-        return load_plugin_config(section);
-    }
-
-    return plugin_config();
-}
-
-plugin_formats config::find_plugin_formats(const std::string& name) const
-{
-    assert(util::is_identifier(name));
-
-    auto section = document_.find(std::string("format.") + name);
-
-    if (section == document_.end())
-        return plugin_formats();
-
-    plugin_formats formats;
-
-    for (const auto& opt : *section)
-        formats.emplace(opt.key(), opt.value());
-
-    return formats;
-}
-
-plugin_paths config::find_plugin_paths(const std::string& name) const
-{
-    assert(util::is_identifier(name));
-
-    auto section = document_.find(std::string("paths.") + name);
-
-    if (section == document_.end())
-        return plugin_paths();
-
-    return read_paths(*section);
-}
-
 bool config::is_verbose() const noexcept
 {
     return util::is_boolean(get(document_, "logs", "verbose"));
@@ -577,36 +512,18 @@
     return servers;
 }
 
-plugin_paths config::load_paths() const
-{
-    auto section = document_.find("paths");
-
-    if (section == document_.end())
-        return {};
-
-    return read_paths(*section);
-}
-
 void config::load_plugins(irccd& irccd) const
 {
     auto it = document_.find("plugins");
 
-    irccd.plugins().set_paths(load_paths());
-
-    if (it != document_.end()) {
-        for (const auto& option : *it) {
-            if (!util::is_identifier(option.key()))
-                continue;
+    if (it == document_.end())
+        return;
 
-            auto paths = find_plugin_paths(option.key());
-
-            if (!paths.empty())
-                irccd.plugins().set_paths(std::move(paths));
+    for (const auto& option : *it) {
+        if (!util::is_identifier(option.key()))
+            continue;
 
-            irccd.plugins().set_config(option.key(), find_plugin_config(option.key()));
-            irccd.plugins().set_formats(option.key(), find_plugin_formats(option.key()));
-            irccd.plugins().load(option.key(), option.value());
-        }
+        irccd.plugins().load(option.key(), option.value());
     }
 }
 
--- a/libirccd/irccd/config.hpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libirccd/irccd/config.hpp	Tue Oct 31 10:48:06 2017 +0100
@@ -68,6 +68,16 @@
     }
 
     /**
+     * Get the underlying document.
+     *
+     * \return the document
+     */
+    inline const ini::document& doc() const noexcept
+    {
+        return document_;
+    }
+
+    /**
      * Get the path to the configuration file.
      *
      * \return the path
@@ -87,30 +97,6 @@
      */
     void load_server_identity(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
-     */
-    plugin_config find_plugin_config(const std::string& name) const;
-
-    /**
-     * Find plugin formats if defined.
-     *
-     * \pre util::isValidIdentifier(name)
-     * \return the formats or empty one if not found
-     */
-    plugin_formats find_plugin_formats(const std::string& name) const;
-
-    /**
-     * Find plugin paths if defined.
-     *
-     * \pre util::isValidIdentifier(name)
-     * \param name the plugin name
-     */
-    plugin_paths find_plugin_paths(const std::string& name) const;
-
     /**
      * Get the path to the pidfile.
      *
@@ -178,13 +164,6 @@
     std::vector<std::shared_ptr<server>> load_servers() const;
 
     /**
-     * Load default paths for plugins.
-     *
-     * \return the map of paths
-     */
-    plugin_paths load_paths() const;
-
-    /**
      * Get the list of defined plugins.
      *
      * \param irccd the irccd instance
--- a/libirccd/irccd/service.cpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libirccd/irccd/service.cpp	Tue Oct 31 10:48:06 2017 +0100
@@ -21,6 +21,7 @@
 #include <functional>
 #include <stdexcept>
 
+#include "config.hpp"
 #include "irccd.hpp"
 #include "logger.hpp"
 #include "service.hpp"
@@ -124,9 +125,6 @@
 plugin_service::plugin_service(irccd& irccd) noexcept
     : irccd_(irccd)
 {
-    default_paths_.emplace("cache", sys::cachedir());
-    default_paths_.emplace("data", sys::datadir());
-    default_paths_.emplace("config", sys::sysconfigdir());
 }
 
 plugin_service::~plugin_service()
@@ -174,68 +172,55 @@
     loaders_.push_back(std::move(loader));
 }
 
-void plugin_service::set_config(const std::string& name, plugin_config config)
+namespace {
+
+template <typename Map>
+Map to_map(const config& conf, const std::string& section)
 {
-    config_.emplace(name, std::move(config));
+    Map ret;
+
+    for (const auto& opt : conf.doc().get(section))
+        ret.emplace(opt.key(), opt.value());
+
+    return ret;
 }
 
-plugin_config plugin_service::config(const std::string& name) const
-{
-    auto it = config_.find(name);
+} // !namespace
 
-    if (it != config_.end())
-        return it->second;
-
-    return plugin_config();
+plugin_config plugin_service::config(const std::string& id)
+{
+    return to_map<plugin_config>(irccd_.config(), util::sprintf("plugin.%s", id));
 }
 
-void plugin_service::set_formats(const std::string& name, plugin_formats formats)
-{
-    formats_.emplace(name, std::move(formats));
-}
-
-plugin_formats plugin_service::formats(const std::string& name) const
+plugin_formats plugin_service::formats(const std::string& id)
 {
-    auto it = formats_.find(name);
-
-    if (it != formats_.end())
-        return it->second;
-
-    return plugin_formats();
+    return to_map<plugin_formats>(irccd_.config(), util::sprintf("format.%s", id));
 }
 
-const plugin_paths& plugin_service::paths() const noexcept
-{
-    return default_paths_;
-}
-
-plugin_paths plugin_service::paths(const std::string& name) const
+plugin_paths plugin_service::paths(const std::string& id)
 {
-    auto result = default_paths_;
-    auto overriden = paths_.find(name);
+    class config cfg(irccd_.config());
 
-    // For all default paths, append the plugin name.
-    for (auto& pair : result)
-        pair.second += "/plugin/"s + name;
+    auto defaults = to_map<plugin_paths>(cfg, "paths");
+    auto paths = to_map<plugin_paths>(cfg, util::sprintf("paths.%s", id));
 
-    // Now, mere overriden paths.
-    if (overriden != paths_.end())
-        for (const auto& pair : overriden->second)
-            result[pair.first] = pair.second;
-
-    return result;
-}
+    // Fill defaults paths.
+    if (!defaults.count("cache"))
+        defaults.emplace("cache", sys::cachedir() + "/plugin/" + id);
+    if (!defaults.count("data"))
+        paths.emplace("data", sys::datadir() + "/plugin/" + id);
+    if (!defaults.count("config"))
+        paths.emplace("config", sys::sysconfigdir() + "/plugin/" + id);
 
-void plugin_service::set_paths(plugin_paths paths)
-{
-    // If the paths is empty or not complete, do not erase default items.
-    for (const auto& pair : paths)
-        default_paths_[pair.first] = pair.second;
-}
+    // Now fill missing fields.
+    if (!paths.count("cache"))
+        paths.emplace("cache", defaults["cache"]);
+    if (!paths.count("data"))
+        paths.emplace("data", defaults["data"]);
+    if (!paths.count("config"))
+        paths.emplace("config", defaults["config"]);
 
-void plugin_service::set_paths(const std::string& name, plugin_paths paths)
-{
-    paths_.emplace(name, std::move(paths));
+    return paths;
 }
 
 std::shared_ptr<plugin> plugin_service::open(const std::string& id,
@@ -277,8 +262,8 @@
             plugin = open(name, std::move(path));
 
         if (plugin) {
-            plugin->set_config(config_[name]);
-            plugin->set_formats(formats_[name]);
+            plugin->set_config(config(name));
+            plugin->set_formats(formats(name));
             plugin->set_paths(paths(name));
             plugin->on_load(irccd_);
 
--- a/libirccd/irccd/service.hpp	Thu Oct 26 20:55:20 2017 +0200
+++ b/libirccd/irccd/service.hpp	Tue Oct 31 10:48:06 2017 +0100
@@ -138,12 +138,8 @@
 class plugin_service {
 private:
     irccd& irccd_;
-    plugin_paths default_paths_;
     std::vector<std::shared_ptr<plugin>> plugins_;
     std::vector<std::unique_ptr<plugin_loader>> loaders_;
-    std::unordered_map<std::string, plugin_config> config_;
-    std::unordered_map<std::string, plugin_formats> formats_;
-    std::unordered_map<std::string, plugin_paths> paths_;
 
 public:
     /**
@@ -210,68 +206,27 @@
     void add_loader(std::unique_ptr<plugin_loader> loader);
 
     /**
-     * Configure a plugin.
+     * Get the configuration for the specified plugin.
      *
-     * If the plugin is already loaded, its configuration is updated.
-     *
-     * \param name the plugin name
-     * \param config the new configuration
+     * \return the configuration
      */
-    void set_config(const std::string& name, plugin_config config);
-
-    /**
-     * Get a configuration for a plugin.
-     *
-     * \param name the plugin name
-     * \return the configuration or default one if not found
-     */
-    plugin_config config(const std::string& name) const;
+    plugin_config config(const std::string& id);
 
     /**
-     * Add formatting for a plugin.
+     * Get the formats for the specified plugin.
      *
-     * \param name the plugin name
-     * \param formats the formats
-     */
-    void set_formats(const std::string& name, plugin_formats formats);
-
-    /**
-     * Get formats for a plugin.
-     *
-     * \param name the plugin name
      * \return the formats
      */
-    plugin_formats formats(const std::string& name) const;
-
-    /**
-     * Get the default paths for plugins.
-     *
-     * \return the paths
-     */
-    const plugin_paths& paths() const noexcept;
+    plugin_formats formats(const std::string& id);
 
     /**
      * Get the paths for the specified plugin.
      *
-     * \param name the plugin
+     * If none is defined, return the default ones.
+     *
      * \return the paths
      */
-    plugin_paths paths(const std::string& name) const;
-
-    /**
-     * Set default paths.
-     *
-     * \param paths the default paths (for all plugins)
-     */
-    void set_paths(plugin_paths paths);
-
-    /**
-     * Override paths for the specified plugin.
-     *
-     * \param name the plugin name
-     * \param paths the paths
-     */
-    void set_paths(const std::string& name, plugin_paths paths);
+    plugin_paths paths(const std::string& id);
 
     /**
      * Generic function for opening the plugin at the given path.