Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 23ffd90dee389df6c29a478db619f3d83bb7caed
  • develop par défaut protégée
  • implement-discord-markdown-update
  • matrix-attachments-order-fix
  • fix-oversized-file-transfer
  • matrix-attachment-order-fix
  • matrix-answer-modified-fix
  • cherry-pick-moise
8 résultats

config.ts

Blame
  • Bifurcation depuis ARISE / matrix-appservice-discord
    Le projet source a une visibilité limitée.
    commands.c 29,86 Kio
    #define _POSIX_C_SOURCE 200809L
    
    #include <common/common.h>
    #include <lektor/commands.h>
    #include <lektor/database.h>
    #include <lektor/net.h>
    #include <lektor/uri.h>
    #include <lektor/cmd.h>
    #include <mthread/mthread.h>
    
    #include <errno.h>
    #include <linux/limits.h>
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.h>
    #include <limits.h>
    #include <poll.h>
    #include <unistd.h>
    
    inline bool
    command_restart(struct lkt_state *srv, size_t c)
    {
        const char *const argv[] = { executable_name, NULL };
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        if (!executable_name) {
            LOG_ERROR_SCT("GENERAL", "%s", "Can't restart if the executable path was not found at start-up");
            return false;
        }
        close(srv->fds[0].fd);
        execv(executable_name, (char *const *) argv);
        LOG_ERROR_SCT("GENERAL", "%s", "Failed to exec lektor or OS not supported");
        return false;
    }
    
    bool
    command_update(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        UNUSED(argv);
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        srv->mpd_idle_events |= MPD_IDLE_UPDATE;
        srv->mpd_idle_events |= MPD_IDLE_DATABASE;
        return ! repo_update(&srv->repo);
    }
    
    static inline void *
    __rescan(void *arg)
    {
        struct lkt_state *srv = arg;
        database_update(srv->db, srv->kara_prefix);
        return NULL;
    }
    
    bool
    command_rescan(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        UNUSED(argv);
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        srv->mpd_idle_events |= MPD_IDLE_UPDATE;
        srv->mpd_idle_events |= MPD_IDLE_DATABASE;
        return ! mthread_create(NULL, ATTR_DETACHED_FREE, __rescan, srv);
    }
    
    inline bool
    command_kill(struct lkt_state *srv, size_t c)
    {
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        LOG_INFO_SCT("GENERAL", "%s", "Stopping lektord");
        close(srv->fds[0].fd);
        exit(EXIT_SUCCESS);
    }
    
    bool
    command_currentsong(struct lkt_state *srv, size_t c)
    {
        struct lkt_message *out;
        struct kara_metadata kara;
        int idx;
    
        memset(&kara, 0, sizeof(struct kara_metadata));
    
        if (!database_queue_current_kara(srv->db, &kara, NULL))
            LOG_ERROR_SCT("COMMAND", "%s", "Failed to get information about the current kara");
    
        out = lkt_message_new();
        idx = snprintf(out->data, LKT_MESSAGE_MAX,
                       "Title: %s\n"
                       "Author: %s\n"
                       "Source: %s\n"
                       "Type: %s%d\n"
                       "Category: %s\n"
                       "Language: %s\n",
                       kara.song_name, kara.author_name, kara.source_name, kara.category,
                       kara.song_number, kara.song_type, kara.language);
        out->data_len = idx;
        lkt_state_send(srv, c, out);
    
        return true;
    }
    
    bool
    command_status(struct lkt_state *srv, size_t c)
    {
        struct lkt_message *out;
        struct lkt_queue_state queue_state;
        struct lkt_win *win;
        int elapsed, duration, songid = 0;
        const char *play_state;
    
        RETURN_UNLESS(srv, "Invalid argument", false);
        win = &srv->win;
        RETURN_UNLESS(database_queue_state(srv->db, &queue_state), "Can't determine playback status", false);
        database_queue_current_kara(srv->db, NULL, &songid);
        win->get_elapsed(win, &elapsed);
        win->get_duration(win, &duration);
    
        out = lkt_message_new();
        play_state = queue_state.current <= 0
                     ? "stop"
                     : (queue_state.paused ? "pause" : "play");
        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX,
                                 "volume: %d\n"
                                 "repeat: %d\n"
                                 "random: %d\n"
                                 "single: %d\n"
                                 "consume: %d\n"
                                 "state: %s\n"
                                 "song: %d\n"
                                 "playlistlength: %d\n"
                                 "elapsed: %d\n"
                                 "duration: %d\n"
                                 "songid: %d\n",
                                 queue_state.volume, queue_state.repeat, queue_state.random,
                                 queue_state.single, queue_state.consume, play_state,
                                 queue_state.current < 0 ? -1 : queue_state.current - 1,
                                 queue_state.length, elapsed, duration, songid);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_next(volatile sqlite3 *db, struct lkt_win *win, mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        char filepath[PATH_MAX];
        return database_queue_next(db, filepath) && win->load_file(win, filepath);
    }
    
    bool
    command_pause(volatile sqlite3 *db, struct lkt_win *win, mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        return database_queue_toggle_pause(db) && win->toggle_pause(win);
    }
    
    bool
    command_previous(volatile sqlite3 *db, struct lkt_win *win, mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        char filepath[PATH_MAX];
        return database_queue_prev(db, filepath) && win->load_file(win, filepath);
    }
    
    static inline bool
    __play_that_file(volatile sqlite3 *db, struct lkt_win *win, int pos)
    {
        char filepath[PATH_MAX];
        RETURN_UNLESS(pos, "Invalid argument", false);
        RETURN_UNLESS(database_queue_play(db, (int) pos), "DB error", false);
        RETURN_UNLESS(database_queue_get_current_file(db, filepath), "Can't get current kara", false);
        RETURN_UNLESS(win->load_file(win, filepath), "Can't load file", false);
        return true;
    }
    
    bool
    command_play(volatile sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        char *endptr, err;
        long pos = 1;
    
        /* Argument handle. */
        if (!args[0]) {
            STRTOL(pos, args[0], endptr, err);
            RETURN_IF(err, "STRTOL failed", false);
        }
    
        /* Do the actual job here. */
        database_queue_stop(db);
        RETURN_UNLESS(win->new (win), "Can't create window", false);
        return __play_that_file(db, win, pos);
    }
    
    bool
    command_playid(volatile sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        char *endptr, err;
        int pos = 0;
        long id;
    
        /* Argument handle. */
        RETURN_IF(args[0] == NULL, "Invalid argument", false);
        STRTOL(id, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        /* Do the work. */
        database_queue_stop(db);
        RETURN_UNLESS(win->new (win), "Can't create window", false);
        database_queue_seekid(db, (int) id, &pos);
        return __play_that_file(db, win, pos);
    }
    
    
    bool
    command_stop(volatile sqlite3 *db, struct lkt_win *win, mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(database_queue_stop(db), "DB error", false);
        win->close(win);
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_add(volatile sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args, "Invalid argument", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        struct lkt_uri uri;
        char *query = args[0];
        int ret, priority = 1;  /* To be modified according to the command (insert or add) later (TODO) */
        UNUSED(win);            /* No callbacks to the window for the moment */
        RETURN_UNLESS(lkt_uri_from(&uri, query), "Failed to parse query", false);
        ret = database_queue_add_uri(db, &uri, priority);
        lkt_uri_free(&uri);
        return ret;
    }
    
    bool
    command_addid(volatile sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        UNUSED(win);
        RETURN_UNLESS(args, "Invalid argument", false);
        long id;
        char *endptr = NULL, err = 0;
        struct lkt_uri uri = { .type = uri_id };
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        STRTOL(id, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
        uri.value = (void *) (size_t) id;
        return database_queue_add_uri(db, &uri, 1);
    }
    
    inline bool
    command_clear(volatile sqlite3 *db, mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return database_queue_clear(db);
    }
    
    inline bool
    command_crop(volatile sqlite3 *db, mpd_idle_flag *watch_mask_ptr)
    {
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return database_queue_crop(db);
    }
    
    bool
    command_delid(volatile sqlite3 *db, struct lkt_win *win, char *id_str, mpd_idle_flag *watch_mask_ptr)
    {
        UNUSED(win);
        long id;
        char *endptr = NULL, err = 0;
        int uri = 0;
    
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        STRTOL(id, id_str, endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        /* If one day we allow suppression of the current kara, will need the `win`
           pointer to reload the kara in the same position (but the kara won't be
           the same). */
        database_queue_current_kara(db, NULL, &uri);
        RETURN_IF(id == (long) uri, "Can't delete current kara", false);
        return database_queue_del_id(db, id);
    }
    
    bool
    command_move(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        long from, to;
        char *endptr, err;
    
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
    
        /* First argument: from */
        STRTOL(from, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        /* Second argument: to */
        STRTOL(to, args[1], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        return database_queue_move(db, from, to);
    }
    
    bool
    command_help(struct lkt_state *srv, size_t c)
    {
        struct lkt_message *out;
        int idx;
        out = lkt_message_new();
        idx = snprintf(out->data, LKT_MESSAGE_MAX, "HELP\n");
        out->data_len = idx;
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_idle(struct lkt_state *srv, size_t c, struct lkt_command *cmd)
    {
        mpd_idle_flag *clt_mask = lkt_client_get_mask(srv, c);
        bool once = false;
    
        for (size_t i = 0; cmd->args[i]; ++i) {
            once |= true;
    
            if (strcmp(cmd->args[i], "database") == 0)
                *clt_mask |= MPD_IDLE_DATABASE;
            else if (strcmp(cmd->args[i], "update") == 0)
                *clt_mask |= MPD_IDLE_UPDATE;
            else if (strcmp(cmd->args[i], "stored_playlist") == 0)
                *clt_mask |= MPD_IDLE_STORED_PLAYLIST;
            else if (strcmp(cmd->args[i], "playlist") == 0)
                *clt_mask |= MPD_IDLE_PLAYLIST;
            else if (strcmp(cmd->args[i], "player") == 0)
                *clt_mask |= MPD_IDLE_PLAYER;
            else if (strcmp(cmd->args[i], "mixer") == 0)
                *clt_mask |= MPD_IDLE_MIXER;
            else if (strcmp(cmd->args[i], "output") == 0)
                *clt_mask |= MPD_IDLE_OUTPUT;
            else if (strcmp(cmd->args[i], "options") == 0)
                *clt_mask |= MPD_IDLE_OPTIONS;
            else if (strcmp(cmd->args[i], "partition") == 0)
                *clt_mask |= MPD_IDLE_PARTITION;
            else if (strcmp(cmd->args[i], "sticker") == 0)
                *clt_mask |= MPD_IDLE_STICKER;
            else if (strcmp(cmd->args[i], "subscription") == 0)
                *clt_mask |= MPD_IDLE_SUBSCRIPTION;
            else if (strcmp(cmd->args[i], "message") == 0)
                *clt_mask |= MPD_IDLE_MESSAGE;
            else
                *clt_mask = MPD_IDLE_ALL;
        }
    
        if (!once)
            *clt_mask = MPD_IDLE_ALL;
    
        LOG_INFO_SCT("COMMAND", "Idle mask for client number %ld is 0x%X", c, *clt_mask);
    
        return true;
    }
    
    inline bool
    command_noidle(struct lkt_state *srv, size_t c)
    {
        mpd_idle_flag *clt_mask = lkt_client_get_mask(srv, c);
        *clt_mask = MPD_IDLE_NONE;
        return true;
    }
    
    /* Functions for the searchadd and the search mpd commands */
    static bool
    lkt_callback_send_row_v1(void *_args, int id, int id_len, const char *sql_row)
    {
        struct lkt_callback *args = (struct lkt_callback *) _args;
        struct lkt_message *out;
        out = lkt_message_new();
        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%*d %s\n", id_len, id, sql_row);
        lkt_state_send(args->srv, args->c, out);
        return true;
    }
    
    static bool
    lkt_callback_send_row_v2(struct lkt_state *srv, size_t c, int id, int id_len, const char *sql_row)
    {
        struct lkt_message *out = lkt_message_new();
        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%*d %s\n", id_len, id, sql_row);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    static bool
    __find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], long continuation,
           enum lkt_find_action action, bool(*init)(volatile sqlite3 *, char *, char *, struct lkt_search *))
    {
        char rgx[PATH_MAX], *col_name, *mpd_tag;
        int count;
        struct lkt_search search = {
            .srv = srv,
            .c = c,
            .continuation = continuation,
            .msg_count = lkt_remaining_msg(srv, c) - 3, /* Reserve slots for OK/ACK and continue: */
        };
    
        /* Check args */
    
        RETURN_UNLESS(cmd_args && cmd_args[0], "Invalid argument", false);
    
        /* Select callback */
    
        switch (action) {
        case LKT_FND_ACT_RESPOND:
            search.call = (void(*)(void)) lkt_callback_send_row_v2;
            break;
        case LKT_FND_ACT_PRINT:
            search.call = NULL;
            break;
        case LKT_FND_ACT_ENQUEUE:
            search.call = (void(*)(void)) lkt_callback_send_row_v2;
            srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
            break;
        case LKT_FND_ACT_ADD:
            search.call = (void(*)(void)) lkt_callback_send_row_v2;
            srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
            break;
        default:
            return false;
        }
    
        /* Select the right column */
    
        mpd_tag = cmd_args[0];
    
        if (!strcasecmp("any", mpd_tag) || !strcasecmp("all", mpd_tag) || !strcasecmp("a", mpd_tag))
            col_name = LKT_DATABASE_KARA_COLUMNT_ANY;
        else if (!strcasecmp("author", mpd_tag))
            col_name = LKT_DATABASE_NAME_KAUTHOR;
        else if (!strcasecmp("source", mpd_tag))
            col_name = LKT_DATABASE_NAME_KNAME;
        else if (!strcasecmp("title", mpd_tag))
            col_name = LKT_DATABASE_NAME_KTITLE;
        else if (!strcasecmp("category", mpd_tag) || !strcasecmp("cat", mpd_tag))
            col_name = LKT_DATABASE_NAME_KCAT;
        else if (!strcasecmp("type", mpd_tag))
            col_name = LKT_DATABASE_NAME_KTYPE;
        else if (!strcasecmp("language", mpd_tag) || !strcasecmp("lang", mpd_tag))
            col_name = LKT_DATABASE_NAME_KLANG;
        else if (!strcasecmp("date", mpd_tag))
            col_name = LKT_DATABASE_NAME_KAUTHOR_YEAR;
        else if (!strcasecmp("id", mpd_tag))
            col_name = LKT_DATABASE_NAME_KID;
        else
            return false;
    
        /* Get the regex */
    
        RETURN_UNLESS(cmd_args[1], "No regex", false);
        memset(rgx, 0, PATH_MAX * sizeof(char));
    
        for (int i = 1; cmd_args[i]; ++i) {
            strncat(rgx, cmd_args[i], PATH_MAX - 1);
            if (cmd_args[i + 1])
                strncat(rgx, " ", PATH_MAX - 1);
        }
    
        /* Make the search langand do the right action */
    
        RETURN_UNLESS(init(srv->db, col_name, rgx, &search), "Failed to init search", false);
    
        for (count = 0; database_search_iter(&search); ++count)
            continue;
    
        if (count)
            lkt_set_continuation(srv, c, continuation + count);
    
        return true;
    }
    
    bool
    command_queue_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], long continuation)
    {
        return __find(srv, c, cmd_args, continuation, LKT_FND_ACT_RESPOND, database_search_queue_init);
    }
    
    bool
    command_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], long continuation,
                 enum lkt_find_action action)
    {
        return __find(srv, c, cmd_args, continuation, action, database_search_database_init);
    }
    
    bool
    command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_option opt,
                                char *args[LKT_MESSAGE_MAX])
    {
        RETURN_UNLESS(srv, "Invalid argument", false);
        UNUSED(c);
        long val;
        char *endptr, ret = false;
        struct lkt_win *win = &srv->win;
    
        if (args == NULL || args[0] == NULL)
            val = 0;
        else {
            STRTOL(val, args[0], endptr, ret);
            RETURN_IF(ret, "STRTOL failed", false);
    
            // No values can exceed those boundings, no matter the option //
            if (val < 0)
                val = 0;
            else if (val > 100)
                val = 100;
        }
    
        switch (opt) {
        case lkt_playback_option_random:
            ret = database_config_queue(srv->db, "random", val);
            break;
        case lkt_playback_option_single:
            ret = database_config_queue(srv->db, "single", val);
            break;
        case lkt_playback_option_consume:
            ret = database_config_queue(srv->db, "consume", val);
            break;
        case lkt_playback_option_repeat:
            ret = database_config_queue(srv->db, "repeat", val);
            break;
        case lkt_playback_option_volume:
            ret = database_config_queue(srv->db, "volume", val);
            LOG_INFO_SCT("COMMAND", "Set volume to %ld", val);
            ret &= win->set_volume(win, val);
            srv->mpd_idle_events |= MPD_IDLE_MIXER;
            break;
        case lkt_playback_option_none:
        default:
            break;
        }
    
        if (ret)
            srv->mpd_idle_events |= MPD_IDLE_OPTIONS;
    
        return ! ret;
    }
    
    bool
    command_set_pos(volatile sqlite3 *db, struct lkt_win *win, mpd_idle_flag *watch_mask_ptr, int index)
    {
        char filepath[PATH_MAX];
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        RETURN_UNLESS(database_queue_set_current_index(db, index), "Failed to set position in queue", false);
        RETURN_UNLESS(database_queue_get_current_file(db, filepath), "Failed to get filename", false);
        return win->load_file(win, filepath);
    }
    
    bool
    command_plt_add(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
    
        bool ret = false;
        struct lkt_uri uri;
    
        if (args[1] == NULL)
            ret = database_plt_create(db, args[0]);
    
        else {
            if (!lkt_uri_from(&uri, args[1])) {
                LOG_ERROR_SCT("COMMAND", "%s", "Failed to get uri");
                goto end_plt_add_uri;
            }
    
            else if (!database_plt_add_uri(db, args[0], &uri)) {
                LOG_ERROR_SCT("COMMAND", "Failed to add uri '%s' to playlist", args[1]);
                goto end_plt_add_uri;
            }
    
            ret = true;
    end_plt_add_uri:
            lkt_uri_free(&uri);
        }
    
        if (ret)
            *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
    
        return ret;
    }
    
    bool
    command_plt_remove(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
        char *endptr, err;
        long pos;
    
        if (args[1] == NULL) {
            *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
            return database_plt_remove(db, args[0]);
        }
    
        STRTOL(pos, args[1], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return database_plt_remove_pos(db, args[0], pos);
    }
    
    bool
    command_plt_clear(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
        RETURN_UNLESS(database_plt_clear(db, args[0]), "Failed to clear playlist", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_plt_rename(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_plt_rename(db, args[0], args[1]), "Failed to rename playlist", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_plt_export(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_attach(db, args[0], args[1]), "Failed to attach database", false);
        RETURN_UNLESS(database_plt_export(db, args[0]), "Failed to export playlist", false);
        RETURN_UNLESS(database_detach(db, args[0]), "Failed to detach database", false);
        LOG_INFO_SCT("COMMAND", "Exported playlist %s with path '%s'", args[0], args[1]);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_plt_import(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_attach(db, args[0], args[1]), "Failed to attach database", false);
        RETURN_UNLESS(database_plt_import(db, args[0]), "Failed to import playlist", false);
        RETURN_UNLESS(database_detach(db, args[0]), "Failed to detach playlist", false);
        LOG_INFO_SCT("COMMAND", "Imported playlist %s with path '%s'", args[0], args[1]);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_plt_add_uri(volatile sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        struct lkt_uri uri;
        RETURN_UNLESS(lkt_uri_from(&uri, args[1]), "Failed to parse uri", false);
        bool ret = database_plt_add_uri(db, args[0], &uri);
        lkt_uri_free(&uri);
        RETURN_UNLESS(ret, "Failed to add uri to plt", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_shuffle(volatile sqlite3 *db, mpd_idle_flag *watch_mask_ptr)
    {
        RETURN_UNLESS(database_queue_shuffle(db), "Failed to shuffle", false);
        *watch_mask_ptr |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_queue_list(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        unsigned int from, to, tmp_switch;
        long val;
        char *endptr, err, *str;
        struct lkt_callback callback = {
            .call = lkt_callback_send_row_v1,
            .srv = srv,
            .c = c,
        };
    
        RETURN_UNLESS(args && args[0] && strlen(args[0]), "Invalid argument", false);
    
        /* Convert the first integer. */
        STRTOL(val, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
        from = labs(val);
    
        /* There is nothing after, is is the song pos version. */
        if (*endptr == '\0')
            goto only_one;
    
        /* There is something after, this is the absolute forme of the command. Then
           parse the second value. Skip the supposed ':' character. */
        else {
            str = endptr;
            STRTOL(val, str, endptr, err);
            RETURN_IF(err, "STRTOL failed", false);
            to = labs(val);
    
            if (to < from) {
                tmp_switch = to;
                to = from;
                from = tmp_switch;
                LOG_INFO_SCT("COMMAND", "Switch range values wrong order, now %d:%d", from, to);
            }
    
            goto is_a_range;
        }
    
        return false;
    
        /* The command is used in its relative forme, display elements from the
           current one. */
    only_one:
        return database_queue_list(srv->db, from, from, &callback);
    
        /* The command is used with a range specifier. */
    is_a_range:
        if (to - from + 1 < lkt_remaining_msg(srv, c) - 2) {
            lkt_set_continuation(srv, c, 0);
            return database_queue_list(srv->db, from, to, &callback);
        } else {
            to = from + lkt_remaining_msg(srv, c) - 3;
            lkt_set_continuation(srv, c, to + 1);
            return database_queue_list(srv->db, from, to, &callback);
        }
    }
    
    bool
    command_password(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(argv[0], "Invalid argument", false);
        RETURN_UNLESS(database_user_authentificate(srv->db, argv[0]), "Failed to authentificate user", false);
        lkt_client_auth(srv, c, true);
        return true;
    }
    
    bool
    command_user_add(struct lkt_state *srv, size_t c, volatile sqlite3 *db, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(argv[0] && argv[1], "Invalid argument", false);
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        RETURN_UNLESS(database_user_add(db, argv[0], argv[1]), "Failed to add user", false);
        return false;
    }
    
    /* Stickers */
    
    static bool
    sticker_send_one_value(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        UNUSED(sticker, type, uri);
        struct sticker_callback *args = (struct sticker_callback *) _args;
        struct lkt_message *out = lkt_message_new();
        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value);
        lkt_state_send(args->srv, args->c, out);
        return false;
    }
    
    static bool
    sticker_send_all(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        struct sticker_callback *args = (struct sticker_callback *) _args;
        struct lkt_message *out = lkt_message_new();
        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\nsticker: %s\nvalue: %d\n",
                                 type, uri, sticker, value);
        lkt_state_send(args->srv, args->c, out);
        return true;
    }
    
    static bool
    sticker_send_check_uri(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        struct sticker_callback *args = (struct sticker_callback *) _args;
        UNUSED(type);
    
        if (uri == args->uri) {
            struct lkt_message *out = lkt_message_new();
            out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\n", sticker, value);
            lkt_state_send(args->srv, args->c, out);
        }
    
        return true;
    }
    
    static bool
    sticker_send_value_check_uri_name(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        struct sticker_callback *args = (struct sticker_callback *) _args;
        struct lkt_message *out;
        UNUSED(type);
    
        if (uri == args->uri || !strcasecmp(sticker, args->name)) {
            out = lkt_message_new();
            out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value);
            lkt_state_send(args->srv, args->c, out);
        }
    
        return true;
    }
    
    static bool
    sticker_check_is_present_eq(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        UNUSED(type);
        struct sticker_callback *args = (struct sticker_callback *) _args;
        args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value == args->value);
        return true;
    }
    
    static bool
    sticker_check_is_present_lt(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        UNUSED(type);
        struct sticker_callback *args = (struct sticker_callback *) _args;
        args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value < args->value);
        return true;
    }
    
    static bool
    sticker_check_is_present_gt(void *_args, const char *sticker, const char *type, int uri, int value)
    {
        UNUSED(type);
        struct sticker_callback *args = (struct sticker_callback *) _args;
        args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value > args->value);
        return true;
    }
    
    bool
    command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(argv[0] && argv[1] && argv[2] && !argv[3], "Invalid argument", false);
        int uri, err;
        char *endptr;
        STRTOL(uri, argv[1], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
        struct sticker_callback cb = {
            .srv = srv,
            .c = c,
            .call = sticker_send_one_value,
        };
        RETURN_UNLESS(database_sticker_get(srv->db, argv[0], argv[2], uri, &cb), "Failed to get sticker", false);
        return true;
    }
    
    bool
    command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        UNUSED(c);
        RETURN_UNLESS(argv[0] && argv[1] && argv[2] && argv[3] && !argv[4], "Invalid argument", false);
        int uri, value, err1, err2;
        char *endptr;
        STRTOL(uri, argv[1], endptr, err1);
        STRTOL(value, argv[4], endptr, err2);
        RETURN_IF(err1 || err2, "STRTOL failed", false);
        RETURN_UNLESS(database_sticker_set(srv->db, argv[0], argv[2], uri, value), "Failed to get sticker", false);
        srv->mpd_idle_events |= MPD_IDLE_STICKER;
        return true;
    }
    
    bool
    command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        struct sticker_callback callback = {
            .srv = srv,
            .c = c,
        };
    
        /* Simple list {type} {uri} command */
        if (argv[0] != NULL && argv[1] != NULL && argv[2] == NULL)
            goto simple_list_command;
    
        /* list {type} {uri} {name} command */
        else if (argv[0] != NULL && argv[1] != NULL &&
                 argv[2] != NULL && argv[3] == NULL)
            goto list_stickers_in_uri;
    
        /* list {type} {uri} {name} `op` {value} command */
        else if (argv[0] != NULL && argv[1] != NULL &&
                 argv[2] != NULL && argv[3] != NULL &&
                 argv[4] != NULL && argv[5] == NULL)
            goto list_stickers_check_value;
    
        /* Just list all stickers */
        else if ( (argv[0] != NULL && argv[1] == NULL) || argv[0] == NULL )
            goto just_list_all;
    
        else
            goto unknown;
    
    just_list_all:
        callback.call = sticker_send_all;
        return database_sticker_list(srv->db, argv[0], &callback);
    
    simple_list_command:
        callback.uri  = atoi(argv[1]);   /* FIXME: Use strtol. */
        callback.call = sticker_send_check_uri;
        return database_sticker_list(srv->db, argv[0], &callback);
    
    list_stickers_in_uri:
        callback.uri  = atoi(argv[1]);  /* FIXME: Use strtol. */
        callback.name = argv[2];
        callback.call = sticker_send_value_check_uri_name;
        return database_sticker_list(srv->db, argv[0], &callback);
        return false;
    
    list_stickers_check_value:
        callback.uri   = atoi(argv[1]); /* FIXME: Use strtol. */
        callback.value = atoi(argv[4]); /* FIXME: Use strtol. */
        callback.name  = argv[2];
        switch (argv[3][0]) {
        case '=':
            callback.call = sticker_check_is_present_eq;
            return database_sticker_list(srv->db, argv[0], &callback);
        case '<':
            callback.call = sticker_check_is_present_lt;
            return database_sticker_list(srv->db, argv[0], &callback);
        case '>':
            callback.call = sticker_check_is_present_gt;
            return database_sticker_list(srv->db, argv[0], &callback);
        default:
            return 0;
        }
    
    unknown:
        LOG_ERROR_SCT("COMMAND", "%s", "Specified command is invalid or unknown");
        return false;
    }
    
    bool
    command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        UNUSED(c);
        RETURN_UNLESS(argv[0] && argv[1], "Invalid argument", false);
        int uri = atoi(argv[1]);
        srv->mpd_idle_events |= MPD_IDLE_STICKER;
        return database_sticker_delete_specify(srv->db, argv[0], uri, argv[2]);
    }