From 3f931bac28a9549bfda1a62f699e967769f11e4b Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Thu, 9 Apr 2020 20:21:47 +0200
Subject: [PATCH] WIP: Changing the way config is stored.

---
 inc/lektor/config.h   |  34 +---
 inc/lektor/database.h |   4 +-
 inc/lektor/net.h      |   2 +-
 meson.build           |   2 +-
 src/config.c          | 219 +++++++++++++++++++++++
 src/config.cpp        | 393 ------------------------------------------
 src/database/config.c |  38 ++++
 src/database/open.c   |  36 ++--
 src/main/lktadm.c     |  24 ++-
 src/main/server.c     |  14 +-
 src/net/listen.c      |  58 +++++--
 11 files changed, 343 insertions(+), 481 deletions(-)
 create mode 100644 src/config.c
 delete mode 100644 src/config.cpp

diff --git a/inc/lektor/config.h b/inc/lektor/config.h
index c517b035..d253fcc8 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);
-
 /* 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 a6dbe24e..303d8499 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);
@@ -101,6 +102,7 @@ bool database_queue_prev(sqlite3 *db, char filepath[PATH_MAX]);
 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);
 
diff --git a/inc/lektor/net.h b/inc/lektor/net.h
index c1ce1b20..c4f3d86d 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 5a881e22..eeb33764 100644
--- a/meson.build
+++ b/meson.build
@@ -40,7 +40,7 @@ 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'
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 00000000..4a4771c0
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,219 @@
+#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 1;
+    }
+
+    return 0;
+}
+
+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_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 = 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(db, conf_file)) {
+        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/config.cpp b/src/config.cpp
deleted file mode 100644
index e5dfb425..00000000
--- 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 77819dea..362ebf6b 100644
--- a/src/database/config.c
+++ b/src/database/config.c
@@ -86,6 +86,44 @@ error:
     return ret;
 }
 
+bool
+database_config_exists(sqlite3 *db, const char *section, const char *key)
+{
+    static const char *SQL_STMT =
+        "SELECT COUNT(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: Failed to insert or replace: %s\n",
+                sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = (0 < sqlite3_column_int(stmt, 1));
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
 bool
 database_config_get_int(sqlite3 *db, const char *section, const char *key, int *value)
 {
diff --git a/src/database/open.c b/src/database/open.c
index 915da6a6..b493bf26 100644
--- a/src/database/open.c
+++ b/src/database/open.c
@@ -32,7 +32,7 @@ static const char *const SQL_MEM_SCHEM =
     "CREATE TABLE IF NOT EXISTS config"
     "  ( section    TEXT NOT NULL"
     "  , key        TEXT NOT NULL"
-    "  , value      TEXT NOT NULL"
+    "  , value      TEXT"
     "  , PRIMARY KEY (section, key)"
     "  ) WITHOUT ROWID;\n";
 
@@ -48,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;
     }
 
@@ -86,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;
     }
 
@@ -101,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/main/lktadm.c b/src/main/lktadm.c
index caddcd63..cd08470c 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 99115376..ba4eea5e 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/net/listen.c b/src/net/listen.c
index f70f6a4a..71072802 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;
     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, " ! error while searching for a config file\n");
+        return 1;
+    }
+
+    if (config_new(srv.db, conf_file)) {
+        fprintf(stderr, " ! failed to read configuration file\n");
+        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,11 +776,6 @@ 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_queue_default(srv.db);
@@ -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))
-- 
GitLab