diff --git a/inc/ini/ini.h b/inc/ini/ini.h
index c95bf7788f2c483b4f61e8ce21844c05e473394c..43a6afdff5dfd0239ca46b333c55ddb5f08a0afb 100644
--- a/inc/ini/ini.h
+++ b/inc/ini/ini.h
@@ -131,7 +131,7 @@ int ini_parse_string(const char *string, ini_handler handler, void *user);
    name and value NULL). Default is to only call the handler on
    each name=value pair. */
 #ifndef INI_CALL_HANDLER_ON_NEW_SECTION
-#define INI_CALL_HANDLER_ON_NEW_SECTION 1
+#define INI_CALL_HANDLER_ON_NEW_SECTION 0
 #endif
 
 /* Nonzero to allow a name without a value (no '=' or ':' on the line) and
diff --git a/inc/ini/ini.hpp b/inc/ini/ini.hpp
deleted file mode 100644
index 763d65a176b9b5a94e136e729301600ec4c9b388..0000000000000000000000000000000000000000
--- a/inc/ini/ini.hpp
+++ /dev/null
@@ -1,74 +0,0 @@
-// Read an INI file into easy-to-access name/value pairs.
-
-// SPDX-License-Identifier: BSD-3-Clause
-
-// Copyright (C) 2009-2020, Ben Hoyt
-
-// inih and INIReader are released under the New BSD license (see LICENSE.txt).
-// Go to the project home page for more info:
-//
-// https://github.com/benhoyt/inih
-
-#ifndef __INIREADER_H__
-#define __INIREADER_H__
-
-#include <map>
-#include <string>
-
-// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
-// for simplicity here rather than speed, but it should be pretty decent.)
-class INIReader
-{
-public:
-    // Construct INIReader and parse given filename. See ini.h for more info
-    // about the parsing.
-    explicit INIReader(const std::string &filename);
-
-    // Construct INIReader and parse given buffer. See ini.h for more info
-    // about the parsing.
-    explicit INIReader(const char *buffer, size_t buffer_size);
-
-    // Return the result of ini_parse(), i.e., 0 on success, line number of
-    // first error on parse error, or -1 on file open error.
-    int ParseError() const;
-
-    // Get a string value from INI file, returning default_value if not found.
-    std::string Get(const std::string &section, const std::string &name,
-                    const std::string &default_value) const;
-
-    // Get a string value from INI file, returning default_value if not found,
-    // empty, or contains only whitespace.
-    std::string GetString(const std::string &section, const std::string &name,
-                          const std::string &default_value) const;
-
-    // Get an integer (long) value from INI file, returning default_value if
-    // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
-    long GetInteger(const std::string &section, const std::string &name, long default_value) const;
-
-    // Get a real (floating point double) value from INI file, returning
-    // default_value if not found or not a valid floating point value
-    // according to strtod().
-    double GetReal(const std::string &section, const std::string &name, double default_value) const;
-
-    // Get a boolean value from INI file, returning default_value if not found or if
-    // not a valid true/false value. Valid true values are "true", "yes", "on", "1",
-    // and valid false values are "false", "no", "off", "0" (not case sensitive).
-    bool GetBoolean(const std::string &section, const std::string &name, bool default_value) const;
-
-    // Return true if the given section exists (section must contain at least
-    // one name=value pair).
-    bool HasSection(const std::string &section) const;
-
-    // Return true if a value exists with the given section and field names.
-    bool HasValue(const std::string &section, const std::string &name) const;
-
-private:
-    int m_error;
-    std::map<std::string, std::string> m_values;
-
-    static std::string MakeKey(const std::string &section, const std::string &name);
-    static int ValueHandler(void *user, const char *section, const char *name,
-                            const char *value);
-};
-
-#endif  // __INIREADER_H__
diff --git a/inc/lektor/config.h b/inc/lektor/config.h
index c517b0358036642fb9fb2e6c2c1eb8598e3de089..56c28206b8a2dd70264469a4908fd9005dc42146 100644
--- a/inc/lektor/config.h
+++ b/inc/lektor/config.h
@@ -1,7 +1,7 @@
 #pragma once
 
 #include <stddef.h>
-
+#include <sqlite3.h>
 
 static const char *const lkt_default_config_file =
     "; Lektor's configuration file\n"
@@ -52,48 +52,22 @@ static const char *const lkt_default_config_file =
     "specific_opt1 = 1\n"
     "specific_opt2 = a string\n";
 
-typedef void *lkt_config_t;
-
 /* It is just an alias to the consecutive use of config_detect_file and
    config_new function, with the return an '&&' between the two returns of the
    two other functions. */
-int config_open(lkt_config_t *conf);
+int config_open(sqlite3 *db);
 
 /* Get the path to the config file that may be red, taking into account the
    priority between existing files. The returned path is a path to an existing
    file. If no file is found, returns a non zero value. Returns 1 otherwise. */
-int config_detect_file(char **conf, size_t conf_len);
-
-/* Simply free a configuration. */
-void config_free(lkt_config_t *conf);
+int config_detect_file(char *conf, size_t conf_len);
 
 /* Create and read the configuration in the conf file and write it into
    lkt_conf. The type is opaque because it's a C++ class.
    Return 0 if the read operation was a success, a non zero value otherwise. */
-int config_new(const char *conf, lkt_config_t *lkt_conf);
+int config_new(sqlite3 *db, const char *conf);
 
 /* Load a module by its name. Also initialize the mod pointer. No check on data
    type is done, it is up to the user to check if the structure passed as a
    `void*` is the right structure. */
-int load_module_by_name(lkt_config_t conf, const char *name, void *mod);
-
-/* Get the value of a parameter in the configuration. The value is stored as a
-   string in the ret pointer. This string will be null-terminated. If the result
-   is longer than the size of ret (as specified by the max_len) the result will
-   be truncated to max_len characters including the null termination byte. */
-int config_get(lkt_config_t conf, const char *section, const char *opt,
-               char *const ret, const size_t max_len);
-
-/* Get the value of a parameter in the configuration as an integer. If there was
-   an error return a non zero value. If the value was retrieved without any error
-   return 0. */
-int config_get_int(lkt_config_t conf, const char *section, const char *opt, int *ret);
-
-/* Get the value of a parameter. The `char*` will be allocated BY THIS FUNCTION,
-   do not malloc or calloc the `char *ret` pointer before.
-   Returns 0 is everything was successfull, a non zero value otherwise. */
-int config_get_alloc(lkt_config_t conf, const char *section, const char *opt, char **ret);
-
-/* Get the value of a parameter as a boolean.
-   Returns 0 is everything was successfull, a non zero value otherwise. */
-int config_get_bool(lkt_config_t conf, const char *section, const char *opt, int *ret);
+int load_module_by_name(sqlite3 *db, const char *name, void *mod);
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index a65a28a94a3ce7c483ba2a9b192ff8fe707491b7..303d8499032baad782b4f1355b3539902ac480c8 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -32,7 +32,8 @@ struct lkt_queue_state {
 };
 
 /* Open correctly a database for lektor. */
-bool database_open(sqlite3 **db, const char *dbpath);
+bool database_new(sqlite3 **db);
+bool database_open(sqlite3 *db, const char *dbpath);
 
 /* Get information on the queue and currently playing kara. */
 bool database_queue_state(sqlite3 *db, struct lkt_queue_state *res);
@@ -98,8 +99,12 @@ bool database_queue_next(sqlite3 *db, char filepath[PATH_MAX]);
 bool database_queue_prev(sqlite3 *db, char filepath[PATH_MAX]);
 
 /* Set a value in the config table */
-bool database_config(sqlite3 *db, const char *option, int value);
-bool database_config_default(sqlite3 *db);
+bool database_config_set(sqlite3 *db, const char *section, const char *key, const char *value);
+bool database_config_get_text(sqlite3 *db, const char *section, const char *key, char *value, size_t len);
+bool database_config_get_int(sqlite3 *db, const char *section, const char *key, int *value);
+bool database_config_exists(sqlite3 *db, const char *section, const char *key);
+bool database_config_queue(sqlite3 *db, const char *option, int value);
+bool database_config_queue_default(sqlite3 *db);
 
 /* Get a value from the config table */
 bool database_get_config(sqlite3 *db, const char *option, int *value);
diff --git a/inc/lektor/net.h b/inc/lektor/net.h
index c1ce1b2031b62544440422b06de4b6a47790d3b4..c4f3d86d77c1d9393dc3b1903e955226b2dfd29f 100644
--- a/inc/lektor/net.h
+++ b/inc/lektor/net.h
@@ -76,4 +76,4 @@ long long int *lkt_client_get_mask(struct lkt_state *srv, size_t c);
 bool lkt_client_auth(struct lkt_state *srv, size_t c, bool set_auth);
 
 /* The server's listen function. */
-int lkt_listen(lkt_config_t conf);
+int lkt_listen(void);
diff --git a/meson.build b/meson.build
index 5a881e22ab03d0a8cc817d85fe770aa70ff49c1b..bf5f440b7dd10c788043d30b27eef1327e540446 100644
--- a/meson.build
+++ b/meson.build
@@ -13,12 +13,9 @@ project( 'lektor'
                           ]
        )
 
-add_languages('cpp', required : true, native : false)
 add_project_arguments('-march=native', language : 'c')
-add_project_arguments('-march=native', language : 'cpp')
 
 cc      = meson.get_compiler('c')
-cxx     = meson.get_compiler('cpp')
 libdl   = cc.find_library('dl')
 dep_x11 = dependency('x11', required : false)
 dep_mpv = dependency('mpv', required : false)
@@ -40,11 +37,10 @@ core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/net/command.c'
                 , 'src/net/listen.c'
                 , 'src/net/message.c'
-                , 'src/config.cpp'
+                , 'src/config.c'
                 , 'src/utils.c'
                 , 'src/uri.c'
                 , 'src/ini/ini.c'
-                , 'src/ini/ini.cpp'
                 , 'src/repo/curl.c'
                 , 'src/repo/async.c'
                 , 'src/thread.c'
diff --git a/src/commands.c b/src/commands.c
index b0db806537bf457c157122d91a7502f7404e98a2..32565f6f04cce2add02f670400e3232a90ab97ff 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -725,19 +725,19 @@ command_set_playback_option(struct lkt_state *srv, size_t c,
 
     switch (opt) {
     case lkt_playback_option_random:
-        ret = database_config(srv->db, "random", val);
+        ret = database_config_queue(srv->db, "random", val);
         break;
     case lkt_playback_option_single:
-        ret = database_config(srv->db, "single", val);
+        ret = database_config_queue(srv->db, "single", val);
         break;
     case lkt_playback_option_consume:
-        ret = database_config(srv->db, "consume", val);
+        ret = database_config_queue(srv->db, "consume", val);
         break;
     case lkt_playback_option_repeat:
-        ret = database_config(srv->db, "repeat", val);
+        ret = database_config_queue(srv->db, "repeat", val);
         break;
     case lkt_playback_option_volume:
-        ret = database_config(srv->db, "volume", val);
+        ret = database_config_queue(srv->db, "volume", val);
         fprintf(stderr, " . command_set_playback_option: set volume to %ld\n", val);
         ret &= win->set_volume(win, val);
         srv->mpd_idle_events |= MPD_IDLE_MIXER;
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000000000000000000000000000000000000..5df978b5ab42d01a0f44ab72695963bc9c7e404d
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,210 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <lektor/config.h>
+#include <lektor/database.h>
+#include <ini/ini.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+static inline int
+load_so(const char *const mod_path, const char *const mod_init, void *mod)
+{
+    /* An init function takes two arguments: the module pointer and a void*
+       which is the handler for the .so file. It the role of the module to
+       store the handle pointer.
+       Uppon successfull completion, the function shall return 0, and return
+       a non zero value if something bad happened. */
+    int (*module_set_function)(void *const, void *const);
+    char *error;
+    void *handle = dlopen(mod_path, RTLD_NOW);
+
+    if (NULL == handle) {
+        fprintf(stderr, " ! load_so: libdl error in dlopen: %s\n", dlerror());
+        return 1;
+    }
+
+    dlerror();
+    *(void **) (&module_set_function) = dlsym(handle, mod_init);
+
+    if ((error = dlerror()) != NULL) {
+        fprintf(stderr, " ! load_so: libdl error in dlsym: %s\n", error);
+        return 1;
+    }
+
+    return ! module_set_function(mod, handle);
+}
+
+inline int
+load_module_by_name(sqlite3 *db, const char *name, void *mod)
+{
+    char mod_path[PATH_MAX], mod_load[INI_MAX_LINE];
+
+    if (!database_config_get_text(db, name, "path", mod_path, PATH_MAX) ||
+        !database_config_get_text(db, name, "load_function", mod_load, INI_MAX_LINE)) {
+        fprintf(stderr, " ! load_module_by_name: Module named %s is incomplete or is not "
+                "defined in config file", name);
+        return 1;
+    }
+
+    return load_so(mod_path, mod_load, mod);
+}
+
+inline static int
+validate_conf(sqlite3 *db)
+{
+#define CHK_OPTION(section, name)                                                               \
+    if (!database_config_exists(db, section, name)) {                                           \
+        fprintf(stderr, " ! validate_conf: Missing option " name " in section " section "\n");  \
+        return 1;                                                                               \
+    }
+
+    CHK_OPTION("externals", "mkvpropedit");
+    CHK_OPTION("externals", "sqlite3");
+
+    CHK_OPTION("server", "host");
+    CHK_OPTION("server", "port");
+    CHK_OPTION("server", "max_clients");
+
+    CHK_OPTION("player", "module");
+    CHK_OPTION("player", "autoclear");
+    CHK_OPTION("player", "def_random");
+    CHK_OPTION("player", "def_consume");
+    CHK_OPTION("player", "def_single");
+    CHK_OPTION("player", "def_repeat");
+
+    CHK_OPTION("database", "kara_dir");
+    CHK_OPTION("database", "db_path");
+
+    return 0;
+}
+
+static int
+#if INI_HANDLER_LINENO
+handler(void *user, const char *section, const char *name, const char *value, int lineno)
+{
+    (void) lineno;
+#else
+handler(void *user, const char *section, const char *name, const char *value)
+{
+#endif
+    if (!section || !name || !value) {
+        fprintf(stderr, " . handler: I can't complete the database with incomplete lines\n");
+        return 1;
+    }
+
+    if (!database_config_set(user, section, name, value)) {
+        fprintf(stderr, " . handler: Failed to update the database\n");
+        return 0;
+    }
+
+    return 1;
+}
+
+int
+config_detect_file(char *conf, size_t conf_len)
+{
+    bool is_malloc = false;
+    struct passwd *pw = getpwuid(getuid());
+    char *home;
+
+    if (conf == NULL)
+        return 1;
+
+    memset(conf, 0, conf_len * sizeof(char));
+
+    /* Try the current working dir config file. */
+    if (getcwd(conf, conf_len - 1) != NULL) {
+        strncat(conf, "/lektor.ini", conf_len - 1);
+        fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+        if (!access(conf, R_OK))
+            goto found;
+    }
+
+    /* Try the config file from the config directory. */
+    home = getenv("XDG_CONFIG_HOME");
+    if (!home || (strlen(home) >= conf_len))
+        home = getenv("HOME");
+    if (!home || (strlen(home) >= conf_len))
+        home = pw->pw_dir;
+    if (!home || (strlen(home) >= conf_len))
+        goto no_config_directory;
+    memcpy(conf, home, (strlen(home) + 1) * sizeof(char));
+    strncat(conf, "/.config/lektor/lektor.ini", conf_len - 1);
+    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    if (!access(conf, R_OK | F_OK))
+        goto found;
+
+no_config_directory:
+    /* Try the '/opt/lektor' file. */
+    memcpy(conf, "/opt/lektor/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
+    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    if (!access(conf, R_OK))
+        goto found;
+
+    /* Try the '/usr/local/etc/lektor.ini' file. */
+    memcpy(conf, "/usr/local/etc/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
+    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    if (!access(conf, R_OK))
+        goto found;
+
+    /* Try the '/etc/lektor.ini' file. */
+    memcpy(conf, "/etc/lektor.ini", sizeof("/etc/lektor.ini"));
+    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    if (!access(conf, R_OK))
+        goto found;
+
+    /* Error */
+    fprintf(stderr, "config_detect_file: an error occured with file %s: %s",
+            conf, strerror(errno));
+    if (is_malloc)
+        free(conf);
+    return 1;
+found:
+    fprintf(stderr, " . config_detect_file: using file %s\n", conf);
+    return 0;
+}
+
+int
+config_new(sqlite3 *db, const char *conf)
+{
+    if (ini_parse(conf, handler, db)) {
+        fprintf(stderr, " . config_new: Failed to parse file %s\n", conf);
+        return 1;
+    }
+
+    if (validate_conf(db)) {
+        fprintf(stderr, " . config_new: Configuration file %s is incomplete\n", conf);
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+config_open(sqlite3 *db)
+{
+    char conf_file[PATH_MAX];
+    int ret = 1;
+
+    if (config_detect_file(conf_file, PATH_MAX)) {
+        fprintf(stderr, " ! config_open: error while searching for a config file\n");
+        goto error;
+    }
+
+    if (config_new(db, conf_file)) {
+        fprintf(stderr, " ! config_open: failed to read configuration file\n");
+        goto error;
+    }
+
+    ret = 0;
+error:
+    return ret;
+}
diff --git a/src/config.cpp b/src/config.cpp
deleted file mode 100644
index e5dfb425216d6b66ffd9ccd916102a1684c1a358..0000000000000000000000000000000000000000
--- a/src/config.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-/* Perform includes. */
-
-extern "C"
-{
-#include <lektor/config.h>
-#include <dlfcn.h>
-#include <stdio.h>
-#include <pwd.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <limits.h>
-}
-
-#include <ini/ini.hpp>
-#include <string>
-#include <cstring>
-
-/* Function to load a .so as a lektor's module. */
-extern "C" inline int
-load_so(const char *const mod_path, const char *const mod_init, void *mod)
-{
-    /* An init function takes two arguments: the module pointer and a void*
-       which is the handler for the .so file. It the role of the module to
-       store the handle pointer.
-       Uppon successfull completion, the function shall return 0, and return
-       a non zero value if something bad happened. */
-    int (*module_set_function)(void *const, void *const);
-    char *error;
-    void *handle = dlopen(mod_path, RTLD_NOW);
-
-    if (NULL == handle) {
-        fprintf(stderr, " ! load_so: libdl error in dlopen: %s\n", dlerror());
-        return 1;
-    }
-
-    dlerror();
-    *(void **) (&module_set_function) = dlsym(handle, mod_init);
-
-    if ((error = dlerror()) != NULL) {
-        fprintf(stderr, " ! load_so: libdl error in dlsym: %s\n", error);
-        return 1;
-    }
-
-    return ! module_set_function(mod, handle);
-}
-
-/* Real functions here, C++ functions. */
-
-inline static void
-__config_free(lkt_config_t *conf)
-{
-    delete reinterpret_cast<INIReader *>(*conf);
-    *conf = nullptr;
-}
-
-inline static int
-validate_conf(const INIReader *const ini)
-{
-    /* Check for mendatory sections. */
-#define CHK_SECTION(name)                                                   \
-    if (!ini->HasSection(name))                                             \
-    {                                                                       \
-        fprintf(stderr, " ! validate_conf: missing section %s\n", name);    \
-        return 1;                                                           \
-    }
-
-    CHK_SECTION("externals");
-    CHK_SECTION("server");
-    CHK_SECTION("player");
-    CHK_SECTION("database");
-
-    /* Check for mendatory options. */
-
-#define CHK_OPTION(section, name)                                                               \
-    if (!ini->HasValue(section, name))                                                          \
-    {                                                                                           \
-        fprintf(stderr, " ! validate_conf: missing option %s in section %s\n", name, section);  \
-        return 1;                                                                               \
-    }
-
-    CHK_OPTION("externals", "mkvpropedit");
-    CHK_OPTION("externals", "sqlite3");
-
-    CHK_OPTION("server", "host");
-    CHK_OPTION("server", "port");
-    CHK_OPTION("server", "max_clients");
-
-    CHK_OPTION("player", "module");
-    CHK_OPTION("player", "autoclear");
-    CHK_OPTION("player", "def_random");
-    CHK_OPTION("player", "def_consume");
-    CHK_OPTION("player", "def_single");
-    CHK_OPTION("player", "def_repeat");
-
-    CHK_OPTION("database", "kara_dir");
-    CHK_OPTION("database", "db_path");
-
-    /* Everything is OK. */
-
-    return 0;
-}
-
-inline static int
-__config_new(const char *conf, lkt_config_t *lkt_conf)
-{
-    INIReader *ini = nullptr;
-    int status;
-
-    try {
-        ini = new INIReader(std::string(conf));
-        *lkt_conf = ini;
-    } catch (...) {
-        return 1;
-    }
-
-    /* Is parsing OK? */
-
-    status = ini->ParseError();
-
-    if (status == -1) {
-        fprintf(stderr, " ! config_new: could not read file %s\n", conf);
-        return 1;
-    }
-
-    if (status > 0) {
-        fprintf(stderr, " ! config_new: error on line %d in file %s\n", status, conf);
-        return 1;
-    }
-
-    /* We need some basic options to be happy here. */
-
-    if (validate_conf(ini)) {
-        fprintf(stderr, " ! config_new: some basic options are missing in file %s\n", conf);
-        return 2;
-    }
-
-    /* Now everything is OK. */
-
-    return 0;
-}
-
-inline static int
-__load_module_by_name(lkt_config_t const conf, const char *name, void *mod)
-{
-    INIReader *ini = reinterpret_cast<INIReader *>(conf);
-
-    if (!ini->HasSection(name)) {
-        fprintf(stderr, " ! load_module_by_name: No module named %s in the conf file\n", name);
-        return 1;
-    }
-
-    std::string mod_path = ini->GetString(name, "path", "");
-    std::string mod_load = ini->GetString(name, "load_function", "");
-
-    if (mod_path.empty() || mod_load.empty()) {
-        fprintf(stderr, " ! load_module_by_name: "
-                "options 'path' and 'load_function' may be absent or empty "
-                "in the section %s\n", name);
-        return 2;
-    }
-
-    return load_so(mod_path.c_str(), mod_load.c_str(), mod);
-}
-
-static int
-__config_get(lkt_config_t conf, const char *section, const char *opt,
-             char *const ret, const size_t max_len)
-{
-    INIReader *ini = reinterpret_cast<INIReader *>(conf);
-
-    if (!ini->HasValue(section, opt))
-        return 1;
-
-    std::string value = ini->GetString(section, opt, "");
-
-    if (value.empty())
-        return 2;
-
-    size_t max_idx = std::min(value.size() + 1, max_len);
-    ret[max_idx] = 0;
-    memcpy(ret, value.c_str(), (max_idx - 1) * sizeof(char));
-
-    return 0;
-}
-
-static int
-__config_get(lkt_config_t conf, const char *section, const char *opt,
-             int *const ret)
-{
-    INIReader *ini = reinterpret_cast<INIReader *>(conf);
-
-    if (!ini->HasValue(section, opt))
-        return 1;
-
-    std::string value = ini->GetString(section, opt, "");
-
-    if (value.empty())
-        return 2;
-
-    try {
-        *ret = std::stoi(value, /* start index */ 0, /* autotedect the base */ 0);
-    } catch (...) {
-        return 3;
-    }
-
-    return 0;
-}
-
-static int
-__config_get(lkt_config_t conf, const char *section, const char *opt,
-             bool *const ret)
-{
-    INIReader *ini = reinterpret_cast<INIReader *>(conf);
-
-    if (!ini->HasValue(section, opt))
-        return 1;
-
-    *ret = ini->GetBoolean(section, opt, false);
-    return 0;
-}
-
-static int
-__config_get(lkt_config_t conf, const char *section, const char *opt, char **ret)
-{
-    INIReader *ini = reinterpret_cast<INIReader *>(conf);
-
-    if (!ini->HasValue(section, opt))
-        return 1;
-
-    std::string value = ini->GetString(section, opt, "");
-
-    if (value.empty())
-        return 2;
-
-    size_t max_idx = value.size();
-
-    /* Yes, use calloc here. */
-    *ret = reinterpret_cast<char *>(calloc(max_idx + 1, sizeof(char)));
-    (*ret)[max_idx] = 0;
-    memcpy(*ret, value.c_str(), max_idx * sizeof(char));
-
-    return 0;
-}
-
-/* Bindings to C functions. */
-
-extern "C"
-{
-    void
-    config_free(lkt_config_t *conf)
-    {
-        __config_free(conf);
-    }
-
-    int
-    config_new(const char *conf, lkt_config_t *lkt_conf)
-    {
-        return __config_new(conf, lkt_conf);
-    }
-
-    int
-    load_module_by_name(lkt_config_t conf, const char *name, void *mod)
-    {
-        return __load_module_by_name(conf, name, mod);
-    }
-
-    int
-    config_get(lkt_config_t conf, const char *section, const char *opt,
-               char *const ret, const size_t max_len)
-    {
-        return __config_get(conf, section, opt, ret, max_len);
-    }
-
-    int
-    config_get_int(lkt_config_t conf, const char *section, const char *opt, int *ret)
-    {
-        return __config_get(conf, section, opt, ret);
-    }
-
-    int
-    config_get_alloc(lkt_config_t conf, const char *section, const char *opt, char **ret)
-    {
-        return __config_get(conf, section, opt, ret);
-    }
-
-    int
-    config_get_bool(lkt_config_t conf, const char *section, const char *opt, int *ret)
-    {
-        bool ret__ = false;
-        int status = __config_get(conf, section, opt, &ret__);
-        *ret = ret__;
-        return status;
-    }
-
-    int
-    config_detect_file(char **conf, size_t conf_len)
-    {
-        bool is_malloc = false;
-        struct passwd *pw = getpwuid(getuid());
-        char *home;
-
-        if (conf == NULL)
-            return 1;
-
-        if (*conf == NULL) {
-            *conf = (char *) calloc(conf_len, sizeof(char));
-
-            if (!*conf)
-                return ENOMEM;
-        }
-
-        memset(*conf, 0, conf_len * sizeof(char));
-
-        /* Try the current working dir config file. */
-        if (getcwd(*conf, conf_len - 1) != NULL) {
-            strncat(*conf, "/lektor.ini", conf_len - 1);
-            fprintf(stderr, " . config_detect_file: trying %s\n", *conf);
-            if (!access(*conf, R_OK))
-                goto found;
-        }
-
-        /* Try the config file from the config directory. */
-        home = getenv("XDG_CONFIG_HOME");
-        if (!home || (strlen(home) >= conf_len))
-            home = getenv("HOME");
-        if (!home || (strlen(home) >= conf_len))
-            home = pw->pw_dir;
-        if (!home || (strlen(home) >= conf_len))
-            goto no_config_directory;
-        memcpy(*conf, home, (strlen(home) + 1) * sizeof(char));
-        strncat(*conf, "/.config/lektor/lektor.ini", conf_len - 1);
-        fprintf(stderr, " . config_detect_file: trying %s\n", *conf);
-        if (!access(*conf, R_OK | F_OK))
-            goto found;
-
-no_config_directory:
-        /* Try the '/opt/lektor' file. */
-        memcpy(*conf, "/opt/lektor/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
-        fprintf(stderr, " . config_detect_file: trying %s\n", *conf);
-        if (!access(*conf, R_OK))
-            goto found;
-
-        /* Try the '/usr/local/etc/lektor.ini' file. */
-        memcpy(*conf, "/usr/local/etc/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
-        fprintf(stderr, " . config_detect_file: trying %s\n", *conf);
-        if (!access(*conf, R_OK))
-            goto found;
-
-        /* Try the '/etc/lektor.ini' file. */
-        memcpy(*conf, "/etc/lektor.ini", sizeof("/etc/lektor.ini"));
-        fprintf(stderr, " . config_detect_file: trying %s\n", *conf);
-        if (!access(*conf, R_OK))
-            goto found;
-
-        /* Error */
-        fprintf(stderr, "config_detect_file: an error occured with file %s: %s",
-                *conf, strerror(errno));
-        if (is_malloc)
-            free(*conf);
-        return 1;
-found:
-        fprintf(stderr, " . config_detect_file: using file %s\n", *conf);
-        return 0;
-    }
-
-    int
-    config_open(lkt_config_t *conf)
-    {
-        char *conf_file = NULL;
-        int ret = 1;
-
-        if (config_detect_file(&conf_file, PATH_MAX)) {
-            fprintf(stderr, " ! config_open: error while searching for a config file\n");
-            goto error;
-        }
-
-        if (config_new(conf_file, conf)) {
-            fprintf(stderr, " ! config_open: failed to read configuration file\n");
-            goto error;
-        }
-
-        ret = 0;
-error:
-        if (conf_file)
-            free(conf_file);
-        return ret;
-    }
-}
diff --git a/src/database/config.c b/src/database/config.c
index 2970a44f60212a311ed3d0d43b3c8c36daa0e0ea..cf4b2eefa9a6e4642639883fe0f435560e01f806 100644
--- a/src/database/config.c
+++ b/src/database/config.c
@@ -1,11 +1,171 @@
+#define _POSIX_C_SOURCE 200809L
+
 #include <lektor/database.h>
 
-#include <linux/limits.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
 bool
-database_config(sqlite3 *db, const char *option, int value)
+database_config_set(sqlite3 *db, const char *section, const char *key, const char *value)
+{
+    static const char *SQL_STMT =
+        "INSERT OR REPLACE INTO"
+        " config (section, key, value)"
+        " VALUES (?, ?, ?);\n";
+    sqlite3_stmt *stmt = 0;
+    bool ret = false;
+    int code;
+
+    if (sqlite3_prepare_v2(db, SQL_STMT, -1, &stmt, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_set: Failed to prepare statement: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    if (sqlite3_bind_text(stmt, 1, section, -1, 0) != SQLITE_OK ||
+        sqlite3_bind_text(stmt, 2, key, -1, 0) != SQLITE_OK     ||
+        sqlite3_bind_text(stmt, 3, value, -1, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_set: Failed to bind: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    code = sqlite3_step(stmt);
+
+    if (code != SQLITE_OK && code != SQLITE_DONE) {
+        fprintf(stderr, " ! database_config_set: Failed to insert or replace: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_config_get_text(sqlite3 *db, const char *section, const char *key, char *value, size_t len)
+{
+    static const char *SQL_STMT =
+        "SELECT value"
+        " FROM config"
+        " WHERE section = ? AND key = ?;\n";
+    sqlite3_stmt *stmt = 0;
+    bool ret = false;
+    int code;
+    char *row;
+
+    if (sqlite3_prepare_v2(db, SQL_STMT, -1, &stmt, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_get_text: Failed to prepare statement: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    if (sqlite3_bind_text(stmt, 1, section, -1, 0) != SQLITE_OK ||
+        sqlite3_bind_text(stmt, 2, key, -1, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_get_text: Failed to bind: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    code = sqlite3_step(stmt);
+
+    if (code != SQLITE_ROW) {
+        fprintf(stderr, " ! database_config_get_text: Failed to insert or replace: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    row = (char *) sqlite3_column_text(stmt, 0);
+    strncpy(value, row, len);
+    value[len - 1] = 0;
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_config_exists(sqlite3 *db, const char *section, const char *key)
+{
+    static const char *SQL_STMT =
+        "SELECT value"
+        " FROM config"
+        " WHERE section = ? AND key = ?;\n";
+    sqlite3_stmt *stmt = 0;
+    bool ret = false;
+    int code;
+
+    if (sqlite3_prepare_v2(db, SQL_STMT, -1, &stmt, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_exists: Failed to prepare statement: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    if (sqlite3_bind_text(stmt, 1, section, -1, 0) != SQLITE_OK ||
+        sqlite3_bind_text(stmt, 2, key, -1, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_exists: Failed to bind: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    code = sqlite3_step(stmt);
+
+    if (code != SQLITE_ROW) {
+        fprintf(stderr, " ! database_config_exists: No rows: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_config_get_int(sqlite3 *db, const char *section, const char *key, int *value)
+{
+    static const char *SQL_STMT =
+        "SELECT CAST(value AS INTEGER)"
+        " FROM config"
+        " WHERE section = ? AND key = ?;\n";
+    sqlite3_stmt *stmt = 0;
+    bool ret = false;
+    int code;
+
+    if (sqlite3_prepare_v2(db, SQL_STMT, -1, &stmt, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_get_int: Failed to prepare statement: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    if (sqlite3_bind_text(stmt, 1, section, -1, 0) != SQLITE_OK ||
+        sqlite3_bind_text(stmt, 2, key, -1, 0) != SQLITE_OK) {
+        fprintf(stderr, " ! database_config_get_int: Failed to bind: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    code = sqlite3_step(stmt);
+
+    if (code != SQLITE_ROW) {
+        fprintf(stderr, " ! database_config_get_int: Failed to insert or replace: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    *value = sqlite3_column_int(stmt, 1);
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_config_queue(sqlite3 *db, const char *option, int value)
 {
     static const char *SQL_STMT_TMP = "UPDATE queue_state SET %s = ? WHERE id = 42;";
     char SQL_STMT[PATH_MAX];
@@ -41,7 +201,7 @@ error:
 }
 
 bool
-database_config_default(sqlite3 *db)
+database_config_queue_default(sqlite3 *db)
 {
     static const char *SQL_DEFAULT =
         "UPDATE queue_state SET"
diff --git a/src/database/open.c b/src/database/open.c
index cdc78a1298e64af7d07f918818f460596a327dc3..b493bf2612155e06d2ec03e0222e73a92068c1b4 100644
--- a/src/database/open.c
+++ b/src/database/open.c
@@ -17,18 +17,24 @@
    This schema is used to initialize the in-memory database. */
 static const char *const SQL_MEM_SCHEM =
     "CREATE TABLE IF NOT EXISTS queue_state"
-    "  ( id          INTEGER PRIMARY KEY DEFAULT 42 CHECK(id = 42)"
-    "  , volume      INTEGER NOT NULL DEFAULT 100 CHECK(0 <= volume AND volume <= 100)"
-    "  , paused      INTEGER NOT NULL DEFAULT 1"
-    "  , random      INTEGER NOT NULL DEFAULT 0"
-    "  , repeat      INTEGER NOT NULL DEFAULT 0"
-    "  , single      INTEGER NOT NULL DEFAULT 0"
-    "  , consume     INTEGER NOT NULL DEFAULT 0"
-    "  , current     INTEGER CHECK(current > 0)"
-    "  , duration    INTEGER CHECK(duration >= 0)"
-    "  , elapsed     INTEGER CHECK(elapsed >= 0)"
+    "  ( id         INTEGER PRIMARY KEY DEFAULT 42 CHECK(id = 42)"
+    "  , volume     INTEGER NOT NULL DEFAULT 100 CHECK(0 <= volume AND volume <= 100)"
+    "  , paused     INTEGER NOT NULL DEFAULT 1"
+    "  , random     INTEGER NOT NULL DEFAULT 0"
+    "  , repeat     INTEGER NOT NULL DEFAULT 0"
+    "  , single     INTEGER NOT NULL DEFAULT 0"
+    "  , consume    INTEGER NOT NULL DEFAULT 0"
+    "  , current    INTEGER CHECK(current > 0)"
+    "  , duration   INTEGER CHECK(duration >= 0)"
+    "  , elapsed    INTEGER CHECK(elapsed >= 0)"
     "  );\n"
-    "INSERT INTO queue_state (id) VALUES (42);\n";
+    "INSERT INTO queue_state (id) VALUES (42);\n"
+    "CREATE TABLE IF NOT EXISTS config"
+    "  ( section    TEXT NOT NULL"
+    "  , key        TEXT NOT NULL"
+    "  , value      TEXT"
+    "  , PRIMARY KEY (section, key)"
+    "  ) WITHOUT ROWID;\n";
 
 #define INVALID_CHARS_DBPATH    ":?!'\""
 #define HEAP_LIMIT_SOFT         100 * 1024 * 1024
@@ -42,25 +48,20 @@ is_sql_str_invalid(const char *str)
 }
 
 bool
-database_open(sqlite3 **db, const char *dbpath)
+database_new(sqlite3 **db)
 {
-    if (is_sql_str_invalid(dbpath)) {
-        fprintf(stderr, " ! database_open: The database path %s is invalid\n", dbpath);
-        return false;
-    }
-
     if (SQLITE_OK != sqlite3_enable_shared_cache(1)) {
-        fprintf(stderr, " ! database_open: Failed to enable shared cache\n");
+        fprintf(stderr, " ! database_new: Failed to enable shared cache\n");
         return false;
     }
 
     if (sqlite3_soft_heap_limit64(HEAP_LIMIT_SOFT) < 0) {
-        fprintf(stderr, " ! database_open: Failed to set soft heap limit to %u\n", HEAP_LIMIT_SOFT);
+        fprintf(stderr, " ! database_new: Failed to set soft heap limit to %u\n", HEAP_LIMIT_SOFT);
         return false;
     }
 
     if (sqlite3_hard_heap_limit64(HEAP_LIMIT_HARD) < 0) {
-        fprintf(stderr, " ! database_open: Failed to set soft heap limit to %u\n", HEAP_LIMIT_HARD);
+        fprintf(stderr, " ! database_new: Failed to set soft heap limit to %u\n", HEAP_LIMIT_HARD);
         return false;
     }
 
@@ -80,14 +81,28 @@ database_open(sqlite3 **db, const char *dbpath)
         goto err_not_init;
     }
 
+    return true;
+err_not_init:
+    *db = NULL;
+    return false;
+}
+
+bool
+database_open(sqlite3 *db, const char *dbpath)
+{
+    if (is_sql_str_invalid(dbpath)) {
+        fprintf(stderr, " ! database_open: The database path %s is invalid\n", dbpath);
+        return false;
+    }
+
     static const char SQL_ATTACH_TEMPLATE[] = "ATTACH '%s' AS disk;";
     size_t len = strlen(dbpath) + (sizeof(SQL_ATTACH_TEMPLATE) / sizeof(char));
     char *SQL_ATTACH = (char *) calloc(len, sizeof(char));
     snprintf(SQL_ATTACH, len - 1, SQL_ATTACH_TEMPLATE, dbpath);
 
-    if (SQLITE_OK != sqlite3_exec(*db, SQL_ATTACH, 0, 0, 0)) {
+    if (SQLITE_OK != sqlite3_exec(db, SQL_ATTACH, 0, 0, 0)) {
         fprintf(stderr, " ! database_open: Failed to attach disk database %s: %s\n",
-                dbpath, sqlite3_errmsg(*db));
+                dbpath, sqlite3_errmsg(db));
         goto err_no_attach;
     }
 
@@ -95,8 +110,5 @@ database_open(sqlite3 **db, const char *dbpath)
 
 err_no_attach:
     free(SQL_ATTACH);
-    sqlite3_close(*db);
-err_not_init:
-    *db = NULL;
     return false;
 }
diff --git a/src/ini/ini.c b/src/ini/ini.c
index 0b66f597079eca28da2b5b99d14628f11b778874..3a480689220d1811f80543b85f071c987f406ece 100644
--- a/src/ini/ini.c
+++ b/src/ini/ini.c
@@ -236,10 +236,14 @@ ini_parse(const char *filename, ini_handler handler, void *user)
     int error;
 
     file = fopen(filename, "r");
-    if (!file)
+    if (!file) {
+        fprintf(stderr, " ! ini_parse: Failed to open file %s\n", filename);
         return -1;
+    }
     error = ini_parse_file(file, handler, user);
     fclose(file);
+    if (error)
+        fprintf(stderr, " ! ini_parse: Got an error, code is %d\n", error);
     return error;
 }
 
diff --git a/src/ini/ini.cpp b/src/ini/ini.cpp
deleted file mode 100644
index 529fa810dabef6aca23e097bc7ecb9d288a061eb..0000000000000000000000000000000000000000
--- a/src/ini/ini.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-// Read an INI file into easy-to-access name/value pairs.
-
-// SPDX-License-Identifier: BSD-3-Clause
-
-// Copyright (C) 2009-2020, Ben Hoyt
-
-// inih and INIReader are released under the New BSD license (see LICENSE.txt).
-// Go to the project home page for more info:
-//
-// https://github.com/benhoyt/inih
-
-#include <algorithm>
-#include <cctype>
-#include <cstdlib>
-#include <ini/ini.h>
-#include <ini/ini.hpp>
-
-using std::string;
-
-INIReader::INIReader(const string &filename)
-{
-    m_error = ini_parse(filename.c_str(), ValueHandler, this);
-}
-
-INIReader::INIReader(const char *buffer, size_t buffer_size)
-{
-    string content(buffer, buffer_size);
-    m_error = ini_parse_string(content.c_str(), ValueHandler, this);
-}
-
-int
-INIReader::ParseError() const
-{
-    return m_error;
-}
-
-string
-INIReader::Get(const string &section, const string &name, const string &default_value) const
-{
-    string key = MakeKey(section, name);
-    return m_values.count(key) ? m_values.find(key)->second : default_value;
-}
-
-string
-INIReader::GetString(const string &section, const string &name, const string &default_value) const
-{
-    const string str = Get(section, name, "");
-    return str.empty() ? default_value : str;
-}
-
-long
-INIReader::GetInteger(const string &section, const string &name, long default_value) const
-{
-    string valstr = Get(section, name, "");
-    const char *value = valstr.c_str();
-    char *end;
-    long n = strtol(value, &end, 0);
-    return end > value ? n : default_value;
-}
-
-double
-INIReader::GetReal(const string &section, const string &name, double default_value) const
-{
-    string valstr = Get(section, name, "");
-    const char *value = valstr.c_str();
-    char *end;
-    double n = strtod(value, &end);
-    return end > value ? n : default_value;
-}
-
-bool
-INIReader::GetBoolean(const string &section, const string &name, bool default_value) const
-{
-    string valstr = Get(section, name, "");
-    std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); // Case-insensitive
-    if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
-        return true;
-    else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
-        return false;
-    else
-        return default_value;
-}
-
-bool
-INIReader::HasSection(const string &section) const
-{
-    const string key = MakeKey(section, "");
-    std::map<string, string>::const_iterator pos = m_values.lower_bound(key);
-    if (pos == m_values.end())
-        return false;
-    // Does the key at the lower_bound pos start with "section"?
-    return pos->first.compare(0, key.length(), key) == 0;
-}
-
-bool
-INIReader::HasValue(const string &section, const string &name) const
-{
-    string key = MakeKey(section, name);
-    return m_values.count(key);
-}
-
-string
-INIReader::MakeKey(const string &section, const string &name)
-{
-    string key = section + "=" + name;
-    std::transform(key.begin(), key.end(), key.begin(), ::tolower); // Case-insensitive
-    return key;
-}
-
-int
-INIReader::ValueHandler(void *user, const char *section, const char *name,
-                        const char *value)
-{
-    if (!name)  // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled
-        return 1;
-    INIReader *reader = static_cast<INIReader *>(user);
-    string key = MakeKey(section, name);
-    if (reader->m_values[key].size() > 0)
-        reader->m_values[key] += "\n";
-    reader->m_values[key] += value ? value : "";
-    return 1;
-}
diff --git a/src/main/lktadm.c b/src/main/lktadm.c
index caddcd63ba54e44c2d16302969110b1bfa4a3f7a..59681e77053fb04a661732e2b3ad409314e76199 100644
--- a/src/main/lktadm.c
+++ b/src/main/lktadm.c
@@ -31,8 +31,7 @@ main(int argc, char *argv[])
     int i, num;
     sqlite3 *db;
     bool ret = true;
-    lkt_config_t conf;
-    char kara_dir[PATH_MAX], *db_path, *mkvpropedit, buf[100];
+    char kara_dir[PATH_MAX], *db_path, mkvpropedit[PATH_MAX], buf[100];
     struct kara_metadata data;
     struct lkt_repo repo;
 
@@ -41,13 +40,18 @@ main(int argc, char *argv[])
 
     /* Read the config file. */
 
-    if (config_open(&conf)) {
+    if (!database_new(&db)) {
+        fprintf(stderr, " ! Failed to initialize memory database\n");
+        return 1;
+    }
+
+    if (config_open(db)) {
         fprintf(stderr, " ! could not load configuration\n");
         return 1;
     }
 
     /* In any case, we won't free the mkvpropedit, this is not a daemon. */
-    if (config_get_alloc(conf, "externals", "mkvpropedit", &mkvpropedit)) {
+    if (!database_config_get_text(db, "externals", "mkvpropedit", mkvpropedit, PATH_MAX)) {
         fprintf(stderr, " ! could not get the 'mkvpropedit property from the externals section\n'");
         return 1;
     }
@@ -55,7 +59,7 @@ main(int argc, char *argv[])
     /* Arguments handle. */
 
     if (strcmp(argv[1], "--init") == 0) {
-        if (config_get(conf, "database", "kara_dir", kara_dir, PATH_MAX)) {
+        if (!database_config_get_text(db, "database", "kara_dir", kara_dir, PATH_MAX)) {
             fprintf(stderr, " ! failed to get the kara directory\n");
             return 10;
         }
@@ -66,17 +70,21 @@ main(int argc, char *argv[])
     else if (strcmp(argv[1], "--populate-all") == 0) {
         db_path = (char *) calloc(PATH_MAX, sizeof(char));
 
-        if (config_get(conf, "database", "db_path", db_path, PATH_MAX)) {
+        if (!database_config_get_text(db, "database", "db_path", db_path, PATH_MAX)) {
             fprintf(stderr, " ! Failed to get database path\n");
             goto end_populate;
         }
 
-        if (config_get(conf, "database", "kara_dir", kara_dir, PATH_MAX)) {
+        if (!database_config_get_text(db, "database", "kara_dir", kara_dir, PATH_MAX)) {
             fprintf(stderr, " ! Failed to get kara directory\n");
             goto end_populate;
         }
 
-        sqlite3_open(db_path, &db);
+    if (!database_open(db, db_path)) {
+        fprintf(stderr, " ! lkt_listen: Failed to open database\n");
+        return 1;
+    }
+
         database_update(db, kara_dir);
         sqlite3_close(db);
 
diff --git a/src/main/server.c b/src/main/server.c
index 991153762a832d1146fced9f68c3742510691c96..ba4eea5e717579761bc9d1d318948ee9afb2365d 100644
--- a/src/main/server.c
+++ b/src/main/server.c
@@ -37,8 +37,6 @@ main(int argc, char *argv[])
 {
     /* Variable initialisation */
     struct passwd *pw = getpwuid(getuid());
-    char *conf_file = NULL;
-    lkt_config_t conf;
 
     /* Argument handleing */
     if (argc <= 1)
@@ -59,15 +57,5 @@ normal_launch:
     fprintf(stderr, " * Lektor launched by user %s (shell: %s, home: %s)\n",
             pw->pw_name, pw->pw_shell, pw->pw_dir);
 
-    if (config_detect_file(&conf_file, PATH_MAX)) {
-        fprintf(stderr, " ! error while searching for a config file\n");
-        return 1;
-    }
-
-    if (config_new(conf_file, &conf)) {
-        fprintf(stderr, " ! failed to read configuration file\n");
-        return 1;
-    }
-
-    return lkt_listen(conf);
+    return lkt_listen();
 }
diff --git a/src/module/module_x11.c b/src/module/module_x11.c
index 39c584ed593e5ca18fcc11d1c298c3505e83a501..25bcb20f0497b89d65b5bb01ca6dbb237da3ceff 100644
--- a/src/module/module_x11.c
+++ b/src/module/module_x11.c
@@ -272,18 +272,18 @@ lmpv_handle(struct lkt_win *win, sqlite3 *db, long long int *mpd_idle_events)
             if (!strcmp(prop->name, "ao-volume")
                 && prop->format == MPV_FORMAT_INT64) {
                 ao_volume = *(int *) prop->data;
-                database_config(db, "volume", ao_volume);
+                database_config_queue(db, "volume", ao_volume);
             }
             // File duration //
             if (!strcmp(prop->name, "duration")
                 && prop->format == MPV_FORMAT_INT64) {
                 win_x11->mpv_duration = *(int *) prop->data;
-                database_config(db, "duration", *(int *) prop->data);
+                database_config_queue(db, "duration", *(int *) prop->data);
             }
             if (!strcmp(prop->name, "time-pos")
                 && prop->format == MPV_FORMAT_INT64) {
                 win_x11->mpv_time_pos = *(int *) prop->data;
-                database_config(db, "elapsed", *(int *) prop->data);
+                database_config_queue(db, "elapsed", *(int *) prop->data);
             }
             // Pause state //
             if (!strcmp(prop->name, "pause")
diff --git a/src/net/listen.c b/src/net/listen.c
index 7c7dc5d59b4c8ee7c00e75d3542b188b8854d2a3..8818ad900ad37e50552ff365d20f1bd37141cb0f 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -4,6 +4,7 @@
 #include <lektor/database.h>
 #include <lektor/repo.h>
 #include <lektor/net.h>
+#include <ini/ini.h>
 
 #include <sqlite3.h>
 
@@ -705,45 +706,69 @@ get_out:
 }
 
 int
-lkt_listen(lkt_config_t conf)
+lkt_listen(void)
 {
     struct lkt_state srv;
     struct lkt_repo repo;
     int autoclear;
+    char *db_path   = (char *) calloc(PATH_MAX, sizeof(char));
+    char *kara_dir  = (char *) calloc(PATH_MAX, sizeof(char));
+    char *host      = (char *) calloc(HOST_NAME_MAX, sizeof(char));
+    char port[7];   /* Maximal port number is 65535, +2 for '\n' and '\0' */
+    char player_mod[INI_MAX_LINE];
+    char conf_file[PATH_MAX];
     memset(&srv, 0, sizeof(struct lkt_state));
 
-    if (config_get_bool(conf, "player", "autoclear", &autoclear)) {
-        fprintf(stderr, " ! Failed to get queue autoclear property\n");
+    /* Initialize the system. */
+
+    if (!database_new(&srv.db)) {
+        fprintf(stderr, " ! lkt_listen: Failed to initialize memory database\n");
         return 1;
     }
 
-    char *db_path   = (char *) calloc(PATH_MAX, sizeof(char));
-    char *kara_dir  = (char *) calloc(PATH_MAX, sizeof(char));
-    char *host      = (char *) calloc(HOST_NAME_MAX, sizeof(char));
-    char port[5];   /* Maximal port number is 65535. */
-    char *player_mod;
+    if (config_detect_file(conf_file, PATH_MAX)) {
+        fprintf(stderr, " ! lkt_listen: error while searching for a config file\n");
+        return 1;
+    }
+
+    if (config_new(srv.db, conf_file)) {
+        fprintf(stderr, " ! lkt_listen: failed to read configuration file %s\n", conf_file);
+        return 1;
+    }
 
-    if (config_get(conf, "database", "db_path", db_path, PATH_MAX)) {
+    if (!database_open(srv.db, db_path)) {
+        fprintf(stderr, " ! lkt_listen: Failed to open database\n");
+        return 1;
+    }
+
+    /* Read the configuration. */
+
+    if (!database_config_get_int(srv.db, "player", "autoclear", &autoclear)) {
+        fprintf(stderr, " ! Failed to get queue autoclear property\n");
+        return 1;
+    }
+
+    if (!database_config_get_text(srv.db, "database", "db_path", db_path, PATH_MAX)) {
         fprintf(stderr, " ! lkt_listen: Failed to get database path\n");
         goto end_free_strings;
     }
 
-    if (config_get(conf, "database", "kara_dir", kara_dir, PATH_MAX)) {
+    if (!database_config_get_text(srv.db, "database", "kara_dir", kara_dir, PATH_MAX)) {
         fprintf(stderr, " ! lkt_listen: Failed to get kara directory\n");
         goto end_free_strings;
     }
 
-    if (config_get(conf, "server", "host", host, HOST_NAME_MAX)) {
+    if (!database_config_get_text(srv.db, "server", "host", host, HOST_NAME_MAX)) {
         fprintf(stderr, " ! lkt_listen: Failed to get the host\n");
         goto end_free_strings;
     }
 
-    if (config_get(conf, "server", "port", port, 5)) {
+    if (!database_config_get_text(srv.db, "server", "port", port, 5)) {
         fprintf(stderr, " ! lkt_listen: Failed to get the port\n");
         goto end_free_strings;
     }
 
-    if (config_get_alloc(conf, "player", "module", &player_mod)) {
+    if (!database_config_get_text(srv.db, "player", "module", player_mod, INI_MAX_LINE)) {
         fprintf(stderr, " ! lkt_listen: Failed to get the module for the player\n");
         goto end_free_strings;
     }
@@ -751,14 +776,9 @@ lkt_listen(lkt_config_t conf)
     if (kara_dir[strlen(kara_dir) - 1] != '/')
         strncat(kara_dir, "/", PATH_MAX - 1);
 
-    if (!database_open(&srv.db, db_path)) {
-        fprintf(stderr, " ! lkt_listen: Failed to open database\n");
-        goto end_free_strings;
-    }
-
     srv.kara_prefix = kara_dir;
 
-    database_config_default(srv.db);
+    database_config_queue_default(srv.db);
 
     srv.fds_max = 16;
     srv.fds = malloc(srv.fds_max * sizeof(struct pollfd));
@@ -774,7 +794,7 @@ lkt_listen(lkt_config_t conf)
     if (autoclear)
         database_queue_clear(srv.db);
 
-    if (!load_module_by_name(conf, player_mod, &srv.win))
+    if (!load_module_by_name(srv.db, player_mod, &srv.win))
         return -2;
 
     if (!srv.win.new(&srv.win))