diff --git a/inc/ini/ini.h b/inc/ini/ini.h
deleted file mode 100644
index 449d2dce0d01f9d0dc8c60f89ee2c6fece17d9ce..0000000000000000000000000000000000000000
--- a/inc/ini/ini.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* inih -- simple .INI file parser
-   SPDX-License-Identifier: BSD-3-Clause
-   Copyright (C) 2009-2020, Ben Hoyt */
-
-#ifndef __INI_H__
-#define __INI_H__
-
-/* Make this header file easier to include in C++ code */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-
-/* Nonzero if ini_handler callback should accept lineno parameter. */
-#ifndef INI_HANDLER_LINENO
-#define INI_HANDLER_LINENO 0
-#endif
-
-/* Typedef for prototype of handler function. */
-#if INI_HANDLER_LINENO
-typedef int (*ini_handler)(void *user, const char *section,
-                           const char *name, const char *value,
-                           int lineno);
-#else
-typedef int (*ini_handler)(void *user, const char *section,
-                           const char *name, const char *value);
-#endif
-
-/* Typedef for prototype of fgets-style reader function. */
-typedef char *(*ini_reader)(char *str, int num, void *stream);
-
-/* Parse given INI-style file. May have [section]s, name=value pairs
-   (whitespace stripped), and comments starting with ';' (semicolon). Section
-   is "" if name=value pair parsed before any section heading. name:value
-   pairs are also supported as a concession to Python's configparser.
-
-   For each name=value pair parsed, call handler function with given user
-   pointer as well as section, name, and value (data only valid for duration
-   of handler call). Handler should return nonzero on success, zero on error.
-
-   Returns 0 on success, line number of first error on parse error (doesn't
-   stop on first error), -1 on file open error, or -2 on memory allocation
-   error (only when INI_USE_STACK is zero).
-*/
-int ini_parse(const char *filename, ini_handler handler, void *user);
-
-/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
-   close the file when it's finished -- the caller must do that. */
-int ini_parse_file(FILE *file, ini_handler handler, void *user);
-
-/* Same as ini_parse(), but takes an ini_reader function pointer instead of
-   filename. Used for implementing custom or string-based I/O (see also
-   ini_parse_string). */
-int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
-                     void *user);
-
-/* Same as ini_parse(), but takes a zero-terminated string with the INI data
-instead of a file. Useful for parsing INI data from a network socket or
-already in memory. */
-int ini_parse_string(const char *string, ini_handler handler, void *user);
-
-/* Nonzero to allow multi-line value parsing, in the style of Python's
-   configparser. If allowed, ini_parse() will call the handler with the same
-   name for each subsequent line parsed. */
-#ifndef INI_ALLOW_MULTILINE
-#define INI_ALLOW_MULTILINE 1
-#endif
-
-/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
-   the file. See https://github.com/benhoyt/inih/issues/21 */
-#ifndef INI_ALLOW_BOM
-#define INI_ALLOW_BOM 1
-#endif
-
-/* Chars that begin a start-of-line comment. Per Python configparser, allow
-   both ; and # comments at the start of a line by default. */
-#ifndef INI_START_COMMENT_PREFIXES
-#define INI_START_COMMENT_PREFIXES ";#"
-#endif
-
-/* Nonzero to allow inline comments (with valid inline comment characters
-   specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
-   Python 3.2+ configparser behaviour. */
-#ifndef INI_ALLOW_INLINE_COMMENTS
-#define INI_ALLOW_INLINE_COMMENTS 1
-#endif
-#ifndef INI_INLINE_COMMENT_PREFIXES
-#define INI_INLINE_COMMENT_PREFIXES ";"
-#endif
-
-/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
-#ifndef INI_USE_STACK
-#define INI_USE_STACK 1
-#endif
-
-/* Maximum line length for any line in INI file (stack or heap). Note that
-   this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
-#ifndef INI_MAX_LINE
-#define INI_MAX_LINE 200
-#endif
-
-/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
-   fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
-   zero. */
-#ifndef INI_ALLOW_REALLOC
-#define INI_ALLOW_REALLOC 0
-#endif
-
-/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
-   is zero. */
-#ifndef INI_INITIAL_ALLOC
-#define INI_INITIAL_ALLOC 200
-#endif
-
-/* Stop parsing on first error (default is to keep parsing). */
-#ifndef INI_STOP_ON_FIRST_ERROR
-#define INI_STOP_ON_FIRST_ERROR 0
-#endif
-
-/* Nonzero to call the handler at the start of each new section (with
-   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 0
-#endif
-
-/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
-   call the handler with value NULL in this case. Default is to treat
-   no-value lines as an error. */
-#ifndef INI_ALLOW_NO_VALUE
-#define INI_ALLOW_NO_VALUE 0
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __INI_H__ */
diff --git a/inc/lektor/common.h b/inc/lektor/common.h
index ebb7faabb2356896afd2c17fd0c5f8a8b32745e5..75f81aba4aac524c50a0ab89b42106c6e5b723d6 100644
--- a/inc/lektor/common.h
+++ b/inc/lektor/common.h
@@ -7,10 +7,13 @@
 
 /* Defines */
 
-#define URL_MAX_LEN     1024
-#define DEFAULT_URL     "https://kurisu.iiens.net"
-#define GET_ID_JSON     DEFAULT_URL "/api_karas.php?id=%ld"
-#define GET_ID_FILE     DEFAULT_URL "/download.php?id=%ld"
+#define INI_MAX_LINE_LEN    512
+#define INI_MAX_SECTION_LEN 80
+
+#define URL_MAX_LEN         1024
+#define DEFAULT_URL         "https://kurisu.iiens.net"
+#define GET_ID_JSON         DEFAULT_URL "/api_karas.php?id=%ld"
+#define GET_ID_FILE         DEFAULT_URL "/download.php?id=%ld"
 
 #define SELF_EXECUTABLE_LINUX           "/proc/self/exe"
 #define SELF_EXECUTABLE_FREEBSD         "/proc/curproc/file"
diff --git a/meson.build b/meson.build
index 3a06d3244e75a7e7d510f9cf1145b1ffa5bae427..5d3d4429352a4851cf0c34246472a178eca591f0 100644
--- a/meson.build
+++ b/meson.build
@@ -58,7 +58,6 @@ core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/net/downloader.c'
                 , 'src/config.c'
                 , 'src/uri.c'
-                , 'src/ini/ini.c'
                 , 'src/thread.c'
                 ]
 
diff --git a/src/config.c b/src/config.c
index cfc6e2b3eaef579628ad42f7a251ba7be3880aeb..e36fcf6bb336b9d205936c17e9795f4f516d2348 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,9 +1,9 @@
 #define _POSIX_C_SOURCE 200809L
 
 #include <common/common.h>
+#include <lektor/common.h>
 #include <lektor/config.h>
 #include <lektor/database.h>
-#include <ini/ini.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
@@ -14,6 +14,103 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <limits.h>
+#include <ctype.h>
+
+typedef int (*ini_handler)(volatile sqlite3 *db, const char *section, const char *name, const char *value);
+
+static inline char *
+strip(char *s)
+{
+    char *p = s + strlen(s);
+    while (p > s && isspace(*(--p)))
+        *p = 0;
+    return s;
+}
+
+static inline char *
+skip(char *s)
+{
+    while (*s && isspace(*s))
+        s++;
+    return s;
+}
+
+static inline int
+ini_parse(const char *path, ini_handler handle, volatile sqlite3 *db)
+{
+    char *start, *end, *name, *value;
+    char section[INI_MAX_SECTION_LEN], line[INI_MAX_LINE_LEN];
+    int error = 0, linenum = 0, len;
+    FILE *file = fopen(path, "r");
+    if (!file) {
+        LOG_ERROR_SCT("PARSER", "Failed to open config file '%s'", path);
+        return 1;
+    }
+
+    /* Parse the file */
+    while (NULL != fgets(line, INI_MAX_LINE_LEN, file)) {
+        ++linenum;
+        start = skip(strip(line));
+
+        /* Skip comments */
+        if (strspn(start, ";#"))
+            continue;
+
+        /* Handle sections */
+        else if (start[0] == '[') {
+            end = &start[1 + strcspn(start + 1, "]")];
+            if (end[0] == ']') {
+                end[0] = '\0';
+                len = strlen(&start[1]);
+                len = MIN(len + 1, INI_MAX_SECTION_LEN);
+                memcpy(section, &start[1], len);
+                section[INI_MAX_SECTION_LEN - 1] = '\0';
+            }
+
+            else {
+                error = 1;
+                LOG_ERROR_SCT("PARSER", "Invalid section name at line '%d'", linenum);
+            }
+        }
+
+        /* Handle name[:=]name pair */
+        else if (start[0]) {
+            end = &start[1 + strcspn(start + 1, ":=")];
+            if (end[0] == '=' || end[0] == ':') {
+                end[0] = '\0';
+                name   = strip(start);
+                value  = &end[1];
+
+                /* Find a comment */
+                end = &value[strcspn(value, ":=")];
+                if (end[0])
+                    end[0] = '\0';
+
+                /* Skip all spaces */
+                value = skip(value);
+                strip(value);
+
+                /* Handle the SECTION, NAME[:=]VALUE */
+                if (handle(db, section, name, value)) {
+                    error = 1;
+                    LOG_ERROR_SCT("PARSER", "Failed to '[handle] %s, %s{:,=}%s' at line '%d'",
+                                  section, name, value, linenum);
+                }
+            }
+
+            else {
+                error = 1;
+                LOG_ERROR_SCT("PARSER", "Invalid name[:=]value pair at line '%d'", linenum);
+            }
+        }
+    }
+
+    /* End of the function */
+    fclose(file);
+    if (error)
+        LOG_ERROR_SCT("PARSER", "An error occured while parsing the file '%s'", path);
+    return error;
+}
 
 int
 load_so(const char *const mod_path, const char *const mod_init, void *mod)
@@ -46,10 +143,10 @@ load_so(const char *const mod_path, const char *const mod_init, void *mod)
 inline int
 load_module_by_name(volatile sqlite3 *db, const char *name, void *mod)
 {
-    char mod_path[PATH_MAX], mod_load[INI_MAX_LINE];
+    char mod_path[PATH_MAX], mod_load[INI_MAX_LINE_LEN];
 
     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)) {
+        !database_config_get_text(db, name, "load_function", mod_load, INI_MAX_LINE_LEN)) {
         LOG_ERROR("Module named %s is incomplete or is not defined in config file", name);
         return 1;
     }
@@ -67,7 +164,6 @@ validate_conf(volatile sqlite3 *db)
     }
 
     CHK_OPTION("externals", "mkvpropedit");
-    CHK_OPTION("externals", "sqlite3");
 
     CHK_OPTION("server", "host");
     CHK_OPTION("server", "port");
@@ -87,17 +183,11 @@ validate_conf(volatile sqlite3 *db)
 }
 
 static int
-#if INI_HANDLER_LINENO
-handler(void *user, const char *section, const char *name, const char *value, int lineno)
+handler(volatile sqlite3 *user, const char *section, const char *name, const char *value)
 {
-    UNUSED(lineno);
-#else
-handler(void *user, const char *section, const char *name, const char *value)
-{
-#endif
     RETURN_UNLESS(section && name && value, "I can't complete the database with incomplete lines", 1);
-    RETURN_UNLESS(database_config_set(user, section, name, value), "Failed to update the database", 0);
-    return 1;
+    RETURN_UNLESS(database_config_set(user, section, name, value), "Failed to update the database", 1);
+    return 0;
 }
 
 int
@@ -166,7 +256,7 @@ found:
 int
 config_new(volatile sqlite3 *db, const char *conf)
 {
-    if (ini_parse(conf, handler, (void *) db)) {
+    if (ini_parse(conf, handler, db)) {
         LOG_ERROR("Failed to parse file %s", conf);
         return 1;
     }
diff --git a/src/ini/ini.c b/src/ini/ini.c
deleted file mode 100644
index 25e3870318e7efb1201ff8e3831067e1777f7e1e..0000000000000000000000000000000000000000
--- a/src/ini/ini.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/* inih -- simple .INI file parser
-   SPDX-License-Identifier: BSD-3-Clause
-   Copyright (C) 2009-2020, Ben Hoyt */
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#include <ini/ini.h>
-
-#if !INI_USE_STACK
-#include <stdlib.h>
-#endif
-
-#define MAX_SECTION 50
-#define MAX_NAME 50
-
-/* Used by ini_parse_string() to keep track of string parsing state. */
-typedef struct {
-    const char *ptr;
-    size_t num_left;
-} ini_parse_string_ctx;
-
-/* Strip whitespace chars off end of given string, in place. Return s. */
-static char *
-rstrip(char *s)
-{
-    char *p = s + strlen(s);
-    while (p > s && isspace((unsigned char)(*--p)))
-        *p = '\0';
-    return s;
-}
-
-/* Return pointer to first non-whitespace char in given string. */
-static char *
-lskip(const char *s)
-{
-    while (*s && isspace((unsigned char)(*s)))
-        s++;
-    return (char *)s;
-}
-
-/* Return pointer to first char (of chars) or inline comment in given string,
-   or pointer to null at end of string if neither found. Inline comment must
-   be prefixed by a whitespace character to register as a comment. */
-static char *
-find_chars_or_comment(const char *s, const char *chars)
-{
-#if INI_ALLOW_INLINE_COMMENTS
-    int was_space = 0;
-    while (*s && (!chars || !strchr(chars, *s)) &&
-           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
-        was_space = isspace((unsigned char)(*s));
-        s++;
-    }
-#else
-    while (*s && (!chars || !strchr(chars, *s)))
-        s++;
-#endif
-    return (char *)s;
-}
-
-/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
-static char *
-strncpy0(char *dest, const char *src, size_t size)
-{
-    strncpy(dest, src, size - 1);
-    dest[size - 1] = '\0';
-    return dest;
-}
-
-/* See documentation in header file. */
-int
-ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
-                 void *user)
-{
-    /* Uses a fair bit of stack (use heap instead if you need to) */
-#if INI_USE_STACK
-    char line[INI_MAX_LINE];
-    int max_line = INI_MAX_LINE;
-#else
-    char *line;
-    size_t max_line = INI_INITIAL_ALLOC;
-#endif
-#if INI_ALLOW_REALLOC && !INI_USE_STACK
-    char *new_line;
-    size_t offset;
-#endif
-    char section[MAX_SECTION] = "";
-    char prev_name[MAX_NAME] = "";
-
-    char *start;
-    char *end;
-    char *name;
-    char *value;
-    int lineno = 0;
-    int error = 0;
-
-#if !INI_USE_STACK
-    line = (char *)malloc(INI_INITIAL_ALLOC);
-    if (!line)
-        return -2;
-#endif
-
-#if INI_HANDLER_LINENO
-#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
-#else
-#define HANDLER(u, s, n, v) handler(u, s, n, v)
-#endif
-
-    /* Scan through stream line by line */
-    while (reader(line, (int)max_line, stream) != NULL) {
-#if INI_ALLOW_REALLOC && !INI_USE_STACK
-        offset = strlen(line);
-        while (offset == max_line - 1 && line[offset - 1] != '\n') {
-            max_line *= 2;
-            if (max_line > INI_MAX_LINE)
-                max_line = INI_MAX_LINE;
-            new_line = realloc(line, max_line);
-            if (!new_line) {
-                free(line);
-                return -2;
-            }
-            line = new_line;
-            if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
-                break;
-            if (max_line >= INI_MAX_LINE)
-                break;
-            offset += strlen(line + offset);
-        }
-#endif
-
-        lineno++;
-
-        start = line;
-#if INI_ALLOW_BOM
-        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
-            (unsigned char)start[1] == 0xBB &&
-            (unsigned char)start[2] == 0xBF)
-            start += 3;
-#endif
-        start = lskip(rstrip(start));
-
-        if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
-            /* Start-of-line comment */
-        }
-#if INI_ALLOW_MULTILINE
-        else if (*prev_name && *start && start > line) {
-            /* Non-blank line with leading whitespace, treat as continuation
-               of previous name's value (as per Python configparser). */
-            if (!HANDLER(user, section, prev_name, start) && !error)
-                error = lineno;
-        }
-#endif
-        else if (*start == '[') {
-            /* A "[section]" line */
-            end = find_chars_or_comment(start + 1, "]");
-            if (*end == ']') {
-                *end = '\0';
-                strncpy0(section, start + 1, sizeof(section));
-                *prev_name = '\0';
-#if INI_CALL_HANDLER_ON_NEW_SECTION
-                if (!HANDLER(user, section, NULL, NULL) && !error)
-                    error = lineno;
-#endif
-            } else if (!error) {
-                /* No ']' found on section line */
-                error = lineno;
-            }
-        } else if (*start) {
-            /* Not a comment, must be a name[=:]value pair */
-            end = find_chars_or_comment(start, "=:");
-            if (*end == '=' || *end == ':') {
-                *end = '\0';
-                name = rstrip(start);
-                value = end + 1;
-#if INI_ALLOW_INLINE_COMMENTS
-                end = find_chars_or_comment(value, NULL);
-                if (*end)
-                    *end = '\0';
-#endif
-                value = lskip(value);
-                rstrip(value);
-
-                /* Valid name[=:]value pair found, call handler */
-                strncpy0(prev_name, name, sizeof(prev_name));
-                if (!HANDLER(user, section, name, value) && !error)
-                    error = lineno;
-            } else if (!error) {
-                /* No '=' or ':' found on name[=:]value line */
-#if INI_ALLOW_NO_VALUE
-                *end = '\0';
-                name = rstrip(start);
-                if (!HANDLER(user, section, name, NULL) && !error)
-                    error = lineno;
-#else
-                error = lineno;
-#endif
-            }
-        }
-
-#if INI_STOP_ON_FIRST_ERROR
-        if (error)
-            break;
-#endif
-    }
-
-#if !INI_USE_STACK
-    free(line);
-#endif
-
-    return error;
-}
-
-/* See documentation in header file. */
-int
-ini_parse_file(FILE *file, ini_handler handler, void *user)
-{
-    return ini_parse_stream((ini_reader)fgets, file, handler, user);
-}
-
-/* See documentation in header file. */
-int
-ini_parse(const char *filename, ini_handler handler, void *user)
-{
-    FILE *file;
-    int error;
-
-    file = fopen(filename, "r");
-    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;
-}
-
-/* An ini_reader function to read the next line from a string buffer. This
-   is the fgets() equivalent used by ini_parse_string(). */
-static char *
-ini_reader_string(char *str, int num, void *stream)
-{
-    ini_parse_string_ctx *ctx = (ini_parse_string_ctx *)stream;
-    const char *ctx_ptr = ctx->ptr;
-    size_t ctx_num_left = ctx->num_left;
-    char *strp = str;
-    char c;
-
-    if (ctx_num_left == 0 || num < 2)
-        return NULL;
-
-    while (num > 1 && ctx_num_left != 0) {
-        c = *ctx_ptr++;
-        ctx_num_left--;
-        *strp++ = c;
-        if (c == '\n')
-            break;
-        num--;
-    }
-
-    *strp = '\0';
-    ctx->ptr = ctx_ptr;
-    ctx->num_left = ctx_num_left;
-    return str;
-}
-
-/* See documentation in header file. */
-int
-ini_parse_string(const char *string, ini_handler handler, void *user)
-{
-    ini_parse_string_ctx ctx;
-
-    ctx.ptr = string;
-    ctx.num_left = strlen(string);
-    return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
-                            user);
-}
diff --git a/src/net/listen.c b/src/net/listen.c
index 30f5e5789f7a32e17ff3dbde969b269ae9560e6f..e72110309974648bd43f5bd519264172ee5d5bfa 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -4,7 +4,6 @@
 #include <lektor/common.h>
 #include <lektor/database.h>
 #include <lektor/net.h>
-#include <ini/ini.h>
 
 #include <sqlite3.h>
 
@@ -697,7 +696,7 @@ lkt_listen(void)
     char *const kara_dir = memory + PATH_MAX + 1;   /* Size is PATH_MAX.        */
     char *const host     = kara_dir + PATH_MAX + 1; /* Size is HOST_NAME_MAX.   */
     char port[7];   /* Maximal port number is 65535, +2 for '\n' and '\0' */
-    char player_mod[INI_MAX_LINE];
+    char player_mod[INI_MAX_LINE_LEN];
     char conf_file[PATH_MAX];
     memset(&srv, 0, sizeof(struct lkt_state));
 
@@ -711,11 +710,11 @@ lkt_listen(void)
     RETURN_UNLESS(database_open(srv.db, db_path), "Can't open database", 1);
 
     /* Read the configuration. */
-    RETURN_UNLESS(database_config_get_int (srv.db, "player",   "autoclear", &autoclear),               "Cfg error", 2);
-    RETURN_UNLESS(database_config_get_text(srv.db, "database", "kara_dir",  kara_dir, PATH_MAX),       "Cfg error", 2);
-    RETURN_UNLESS(database_config_get_text(srv.db, "server",   "host",      host, HOST_NAME_MAX),      "Cfg error", 2);
-    RETURN_UNLESS(database_config_get_text(srv.db, "server",   "port",      port, 5),                  "Cfg error", 2);
-    RETURN_UNLESS(database_config_get_text(srv.db, "player",   "module",    player_mod, INI_MAX_LINE), "Cfg error", 2);
+    RETURN_UNLESS(database_config_get_int (srv.db, "player",   "autoclear", &autoclear),                   "Cfg error", 2);
+    RETURN_UNLESS(database_config_get_text(srv.db, "database", "kara_dir",  kara_dir, PATH_MAX),           "Cfg error", 2);
+    RETURN_UNLESS(database_config_get_text(srv.db, "server",   "host",      host, HOST_NAME_MAX),          "Cfg error", 2);
+    RETURN_UNLESS(database_config_get_text(srv.db, "server",   "port",      port, 5),                      "Cfg error", 2);
+    RETURN_UNLESS(database_config_get_text(srv.db, "player",   "module",    player_mod, INI_MAX_LINE_LEN), "Cfg error", 2);
 
     if (kara_dir[strlen(kara_dir) - 1] != '/')
         strncat(kara_dir, "/", PATH_MAX - 1);