Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 575db8295af0c1f15a127189d2fb3202f4d5c484
  • master par défaut protégée
  • rust-playlist-sync
  • rust
  • fix-qt-deprecated-qvariant-type
  • fix-mpris-qtwindow-race-condition
  • rust-appimage-wayland
  • windows-build-rebased
  • v2.5 protégée
  • v2.4 protégée
  • v2.3-1 protégée
  • v2.3 protégée
  • v2.2 protégée
  • v2.1 protégée
  • v2.0 protégée
  • v1.8-3 protégée
  • v1.8-2 protégée
  • v1.8-1 protégée
  • v1.8 protégée
  • v1.7 protégée
  • v1.6 protégée
  • v1.5 protégée
  • v1.4 protégée
  • v1.3 protégée
  • v1.2 protégée
  • v1.1 protégée
  • v1.0 protégée
27 résultats

commands.c

Blame
  • commands.c 31,09 Kio
    #define _POSIX_C_SOURCE 200809L
    
    #include <lektor/common.h>
    #include <lektor/commands.h>
    #include <lektor/database.h>
    #include <lektor/net.h>
    #include <lektor/uri.h>
    #include <lektor/cmd.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,
                    char __attribute__((unused)) *__argv[LKT_MESSAGE_ARGS_MAX])
    {
        const char *const argv[] = { executable_name, NULL };
        struct lkt_queue_state sta = {0};
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        if (!executable_name) {
            LOG_ERROR("GENERAL", "Can't restart if the executable path was not found at start-up");
            return false;
        }
        close(srv->fds[0].fd);
        database_queue_state(srv->db, &sta);
        lkt_queue_free(&srv->queue);
        env_set(LKT_ENV_RESTART, "1");
        int len = long_length(LONG_MAX);
        if (len > 0) {
            char *pos = calloc(len, sizeof(char));
            if (pos) {
                safe_snprintf(pos, len, "%d", sta.current);
                env_set(LKT_ENV_CURRENT, pos);
                free(pos);
            } else
                LOG_WARN("GENERAL", "Failed to malloc, don't set %s to %d",
                         LKT_ENV_CURRENT, sta.current);
        } else
            env_set(LKT_ENV_CURRENT, "NULL");
        database_close_all();
        execv(executable_name, (char *const *) argv);
        LOG_ERROR("GENERAL", "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 ! MOD_PROC(srv->repo_mod, "update");
    }
    
    bool
    command_rescan(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX], int forced)
    {
        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 ! MOD_PROC(srv->repo_mod, "rescan");
    }
    
    bool
    command_kill(struct lkt_state *srv, size_t c,
                 char __attribute__((unused)) *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        LOG_INFO("GENERAL",  "Stopping lektord");
        MOD_PROC(srv->repo_mod, "free");
        MOD_PROC(srv->window_mod, "free");
        close(srv->fds[0].fd);
        lkt_queue_free(&srv->queue);
        database_close_all();
        LOG_INFO("GENERAL", "lektord will now exit");
        exit(EXIT_SUCCESS);
    }
    
    bool
    command_currentsong(struct lkt_state *srv, size_t c,
                        char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        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("COMMAND", "Failed to get information about the current kara");
    
        out = lkt_message_new();
        idx = safe_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.song_type,
                            kara.song_number, kara.category, kara.language);
        out->data_len = idx;
        lkt_state_send(srv, c, out);
    
        return true;
    }
    
    bool
    command_status(struct lkt_state *srv, size_t c,
                   char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        struct lkt_message *out;
        struct lkt_queue_state queue_state;
        int elapsed, duration, songid = 0;
        const char *play_state;
    
        RETURN_UNLESS(srv, "Invalid argument", false);
        RETURN_UNLESS(database_queue_state(srv->db, &queue_state), "Can't get playback status", false);
        database_queue_current_kara(srv->db, NULL, &songid);
        MOD_CALL(srv->window_mod, "get_elapsed", &elapsed);
        MOD_CALL(srv->window_mod, "get_duration", &duration);
    
        out = lkt_message_new();
        play_state = queue_state.current <= 0
                     ? "stop"
                     : (queue_state.paused ? "pause" : "play");
        out->data_len = safe_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(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        char filepath[PATH_MAX];
        if (!database_queue_next(srv->db, filepath))
            return false;
        if (MOD_CALL(srv->window_mod, "load", filepath))
            return false;
        else {
            config_handle_hook(srv->db, "kara_load");
            return true;
        }
    }
    
    bool
    command_pause(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        if (!database_queue_toggle_pause(srv->db))
            return false;
        return ! MOD_PROC(srv->window_mod, "toggle");
    }
    
    bool
    command_previous(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        char filepath[PATH_MAX];
        if (!database_queue_prev(srv->db, filepath))
            return false;
        if (MOD_CALL(srv->window_mod, "load", filepath))
            return false;
        else {
            config_handle_hook(srv->db, "kara_load");
            return true;
        }
    }
    
    static inline bool
    __play_that_file(struct lkt_state *win, int pos)
    {
        char filepath[PATH_MAX];
        RETURN_UNLESS(pos, "Invalid argument", false);
        RETURN_UNLESS(database_queue_play(win->db, (int) pos), "DB error", false);
        RETURN_UNLESS(database_queue_get_current_file(win->db, filepath), "Can't get current kara", false);
        RETURN_IF(MOD_CALL(win->window_mod, "load", filepath), "Can't load file", false);
        return true;
    }
    
    bool
    command_play(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        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(srv->db);
        RETURN_IF(MOD_CALL(srv->window_mod, "new", &srv->queue, srv->db), "Can't create window", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
    
        /* Hooks */
        if (__play_that_file(srv, pos)) {
            config_handle_hook(srv->db, "queue_begin");
            config_handle_hook(srv->db, "kara_load");
            return true;
        } else
            return false;
    }
    
    bool
    command_dump(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args[0], "Invalid argument", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return database_queue_dump(srv->db, args[0]);
    }
    
    bool
    command_playid(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        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(srv->db);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        RETURN_IF(MOD_CALL(srv->window_mod, "new", &srv->queue, srv->db), "Can't create window", false);
        database_queue_seekid(srv->db, (int) id, &pos);
    
        /* Hooks */
        if (__play_that_file(srv, pos)) {
            config_handle_hook(srv->db, "queue_begin");
            config_handle_hook(srv->db, "kara_load");
            return true;
        } else
            return false;
    }
    
    
    bool
    command_stop(struct lkt_state *srv, char __attribute__((unused))  *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(database_queue_stop(srv->db), "DB error on stop", false);
        MOD_PROC(srv->window_mod, "close");
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_add(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX], int priority)
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
        struct lkt_uri uri;
        int ret; /* To be modified according to the command (insert or add) later (TODO) */
        RETURN_UNLESS(lkt_uri_from(&uri, args[0]), "Failed to parse query", false);
        ret = database_queue_add_uri(srv->db, &uri, priority);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        lkt_uri_free(&uri);
        if (!ret)
            LOG_ERROR("COMMAND", "Failed to add with priority %d in queue", priority);
        return ret;
    }
    
    bool
    command_addid(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args, "Invalid argument", false);
        errno = 0;
        int i;
        struct lkt_uri uri = { .type = uri_id };
        for (i = 0; (uri.id = strtol(args[i], NULL, 0)) && ! errno; ++i)
            errno |= database_queue_add_id(srv->db, uri.id, 1);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return ! errno;
    }
    
    inline bool
    command_clear(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return command_stop(srv, args) &&
               database_queue_clear(srv->db);
    }
    
    inline bool
    command_crop(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return database_queue_crop(srv->db);
    }
    
    bool
    command_delid(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long id;
        char *endptr = NULL, err = 0, filepath[PATH_MAX];
        int uri = 0;
    
        RETURN_UNLESS(args[0], "No id provided", false);
        STRTOL(id, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        database_queue_current_kara(srv->db, NULL, &uri);
    
        if (id == (long) uri) {
            if (database_queue_skip_current(srv->db, filepath)) {
                if (MOD_CALL(srv->window_mod, "load", filepath)) {
                    LOG_ERROR("COMMAND", "Failed to skip current kara to delete id %ld", id);
                    return false;
                }
            }
    
            else {
                LOG_WARN("COMMAND", "Failed to skip current kara to delete id %ld, stop playback", id);
                MOD_PROC(srv->window_mod, "close");
            }
        }
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return database_queue_del_id(srv->db, id);
    }
    
    bool
    command_move(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long from, to;
        char *endptr, err;
    
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
    
        /* 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);
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return database_queue_move(srv->db, from, to);
    }
    
    bool
    command_help(struct lkt_state *srv, size_t c,
                 char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        struct lkt_message *out;
        out = lkt_message_new();
        out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "HELP\n");
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_idle(struct lkt_state *srv, size_t c, struct lkt_command *cmd)
    {
        bool once;
        size_t i;
    
        for (once = false, i = 0; cmd->args[i]; ++i, once |= true) {
            if (STR_MATCH(cmd->args[i], "database"))
                lkt_client_add_mask(srv, c, MPD_IDLE_DATABASE);
            else if (STR_MATCH(cmd->args[i], "update"))
                lkt_client_add_mask(srv, c, MPD_IDLE_UPDATE);
            else if (STR_MATCH(cmd->args[i], "stored_playlist"))
                lkt_client_add_mask(srv, c, MPD_IDLE_STORED_PLAYLIST);
            else if (STR_MATCH(cmd->args[i], "playlist"))
                lkt_client_add_mask(srv, c, MPD_IDLE_PLAYLIST);
            else if (STR_MATCH(cmd->args[i], "player"))
                lkt_client_add_mask(srv, c, MPD_IDLE_PLAYER);
            else if (STR_MATCH(cmd->args[i], "mixer"))
                lkt_client_add_mask(srv, c, MPD_IDLE_MIXER);
            else if (STR_MATCH(cmd->args[i], "output"))
                lkt_client_add_mask(srv, c, MPD_IDLE_OUTPUT);
            else if (STR_MATCH(cmd->args[i], "options"))
                lkt_client_add_mask(srv, c, MPD_IDLE_OPTIONS);
            else if (STR_MATCH(cmd->args[i], "partition"))
                lkt_client_add_mask(srv, c, MPD_IDLE_PARTITION);
            else if (STR_MATCH(cmd->args[i], "sticker"))
                lkt_client_add_mask(srv, c, MPD_IDLE_STICKER);
            else if (STR_MATCH(cmd->args[i], "subscription"))
                lkt_client_add_mask(srv, c, MPD_IDLE_SUBSCRIPTION);
            else if (STR_MATCH(cmd->args[i], "message"))
                lkt_client_add_mask(srv, c, MPD_IDLE_MESSAGE);
            else
                lkt_client_add_mask(srv, c, MPD_IDLE_ALL);
        }
    
        if (!once)
            lkt_client_add_mask(srv, c, MPD_IDLE_ALL);
    
        LOG_INFO("COMMAND", "Idle mask for client number %ld is 0x%X", c, lkt_client_get_mask(srv, c));
    
        return true;
    }
    
    inline bool
    command_noidle(struct lkt_state *srv, size_t c)
    {
        lkt_client_clear_mask(srv, c);
        return true;
    }
    
    /* Functions for the searchadd and the search mpd commands */
    bool
    lkt_callback_send_row_v1(void *_args, int pos_len, int pos, 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 = safe_snprintf(out->data, LKT_MESSAGE_MAX, "%*d: %*d %s\n",
                                      pos_len, pos, id_len, id, sql_row);
        lkt_state_send(args->srv, args->c, out);
        return true;
    }
    
    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 = safe_snprintf(out->data, LKT_MESSAGE_MAX, "%*d %s\n", id_len, id, sql_row);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    lkt_callback_send_list_plts(struct lkt_state *srv, size_t c, const char *plt_name)
    {
        struct lkt_message *out = lkt_message_new();
        /* If the playlist is named OK or ACK... */
        out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "name: %s\n", plt_name);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    static bool
    command_findid(struct lkt_state *srv, size_t c, char *id_str)
    {
        double duration;
        struct kara_metadata kara = {};
        char filepath[PATH_MAX] = {0};
        long id = strtol(id_str, NULL, 0); /* XXX: Must be valid here */
        if (!database_kara_by_id(srv->db, id, &kara, filepath)) {
            LOG_ERROR("COMMAND", "Can't get a kara with id %ld", id);
            return false;
        }
        if (kara_read_length(&duration, filepath)) {
            LOG_WARN("COMMAND", "Can't get duration of kara %ld", id);
            duration = 0.;
        }
        int s = duration * 10e-10;
        int h = s / 3600;
        s = s % 3600;
        int m = s / 60;
        s = s % 60;
        struct lkt_message *out = lkt_message_new();
        out->data_len = safe_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"
                                      "Duration: %d:%02d:%02d\n",
                                      kara.song_name, kara.author_name,
                                      kara.source_name, kara.song_type,
                                      kara.song_number, kara.category,
                                      kara.language, h, m, s);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    static inline bool
    __iter_search(struct lkt_search *search)
    {
        int count;
        for (count = 0; database_search_iter(search); ++count)
            continue;
    
        if (count)
            lkt_set_continuation(search->srv, search->c, search->continuation + count);
        else
            LOG_WARN("COMMAND", "Nothing found");
        return true;
    }
    
    bool
    command_find(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX],
                 long continuation, bool(*init)(volatile sqlite3 *, struct lkt_search *))
    {
        int count;
        struct lkt_search search = {
            .srv = srv,
            .c = c,
            .continuation = continuation,
            /* Reserve slots for OK/ACK and continue: */
            .msg_count = lkt_remaining_msg(srv, c) - 3,
            .call = (void(*)(void)) lkt_callback_send_row_v2,
            .ka_uri = {0},
            .plt_name = args[0], /* In case of playlist searchs */
        };
    
        RETURN_UNLESS(args[0], "Invalid argument", false);
    
        /* Just an id */
        if (!args[1] && (count = strtol(args[0], NULL, 0)))
            return command_findid(srv, c, args[0]);
    
        /* With an URI */
        if (!lkt_uri_from_list(&search.ka_uri, args)) {
            /* Try from idx 1, in case of playlust searches */
            LOG_DEBUG("COMMAND", "URI may not starts at idx 0, may be because "
                      "of playlist search. At idx 0, value was '%s'", args[0]);
            RETURN_UNLESS(lkt_uri_from_list(&search.ka_uri, &args[1]),
                          "Failed to create the uri", false);
        }
    
        RETURN_UNLESS(init(srv->db, &search), "Failed to init search", false);
        return __iter_search(&search);
    }
    
    bool
    command_plt_list(struct lkt_state *srv, size_t c,
                     char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX],
                     int cont)
    {
        struct lkt_search search = {
            .srv          = srv,
            .c            = c,
            .continuation = cont,
            .msg_count    = lkt_remaining_msg(srv, c) - 3, /* Slots for OK/ACK and continuation */
            .call         = (void(*)(void)) lkt_callback_send_list_plts,
        };
    
        RETURN_UNLESS(database_search_listplaylist_init(srv->db, &search), "Failed to init search", false);
        return __iter_search(&search);
    }
    
    bool
    command_plt_ctx(struct lkt_state *srv, size_t c,
                    char *args[LKT_MESSAGE_ARGS_MAX], int cont)
    {
        struct lkt_search search = {
            .srv          = srv,
            .c            = c,
            .continuation = cont,
            .msg_count    = lkt_remaining_msg(srv, c) - 3,
            .call         = (void(*)(void)) lkt_callback_send_row_v2,
            .plt_name     = args[0],
            .ka_uri       = { .type = uri_null },
        };
    
        RETURN_UNLESS(database_search_playlist_init(srv->db, &search), "Failed to init search", false);
        return __iter_search(&search);
    }
    
    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;
    
        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("COMMAND", "Set volume to %ld", val);
            ret &= ! MOD_CALL(srv->window_mod, "set_volume", 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(struct lkt_state *srv, int index)
    {
        char filepath[PATH_MAX];
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        RETURN_UNLESS(database_queue_set_current_index(srv->db, index),
                      "Failed to set position in queue", false);
        RETURN_UNLESS(database_queue_get_current_file(srv->db, filepath),
                      "Failed to get filename", false);
        return ! MOD_CALL(srv->window_mod, "load", filepath);
    }
    
    bool
    command_plt_add(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
    
        bool ret = false;
        struct lkt_uri uri;
    
        if (args[1] == NULL)
            ret = database_plt_create(srv->db, args[0]);
    
        else {
            if (!lkt_uri_from(&uri, args[1])) {
                LOG_ERROR("COMMAND", "Failed to get uri");
                goto end_plt_add_uri;
            }
    
            else if (!database_plt_add_uri(srv->db, args[0], &uri)) {
                LOG_ERROR("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)
            srv->mpd_idle_events |= MPD_IDLE_PLAYER;
    
        return ret;
    }
    
    bool
    command_plt_remove(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
        char *endptr, err;
        long pos;
    
        if (args[1] == NULL) {
            srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
            return database_plt_remove(srv->db, args[0]);
        }
    
        STRTOL(pos, args[1], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return database_plt_remove_pos(srv->db, args[0], pos);
    }
    
    bool
    command_plt_clear(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0], "Invalid argument", false);
        RETURN_UNLESS(database_plt_clear(srv->db, args[0]), "Failed to clear playlist", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_plt_rename(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_plt_rename(srv->db, args[0], args[1]), "Failed to rename playlist", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_plt_export(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_attach(srv->db, args[0], args[1]), "Failed to attach database", false);
        RETURN_UNLESS(database_plt_export(srv->db, args[0]), "Failed to export playlist", false);
        RETURN_UNLESS(database_detach(srv->db, args[0]), "Failed to detach database", false);
        LOG_INFO("COMMAND", "Exported playlist %s with path '%s'", args[0], args[1]);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_plt_import(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        RETURN_UNLESS(database_attach(srv->db, args[0], args[1]), "Failed to attach database", false);
        RETURN_UNLESS(database_plt_import(srv->db, args[0]), "Failed to import playlist", false);
        RETURN_UNLESS(database_detach(srv->db, args[0]), "Failed to detach playlist", false);
        LOG_INFO("COMMAND", "Imported playlist %s with path '%s'", args[0], args[1]);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_plt_add_uri(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        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(srv->db, args[0], &uri);
        lkt_uri_free(&uri);
        RETURN_UNLESS(ret, "Failed to add uri to plt", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return true;
    }
    
    bool
    command_shuffle(struct lkt_state *srv, char __attribute__((unused)) *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(database_queue_shuffle(srv->db), "Failed to shuffle", false);
        srv->mpd_idle_events |= 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 (from)", 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 + strspn(endptr, "-+: ");
            STRTOL(val, str, endptr, err);
            RETURN_IF(err, "STRTOL failed (to)", false);
            to = labs(val);
    
            if (to < from) {
                tmp_switch = to;
                to = from;
                from = tmp_switch;
                LOG_INFO("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) {
            LOG_INFO("COMMAND", "Got range %d:%d, no continuation needed", from, to);
            lkt_set_continuation(srv, c, 0);
            return database_queue_list(srv->db, from, to, &callback);
        } else {
            LOG_INFO("COMMAND", "Got range %d:%d, continuation needed", from, to);
            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 auth user", false);
        lkt_client_auth(srv, c, true);
        return true;
    }
    
    bool
    command_user_add(struct lkt_state *srv, size_t c, 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(srv->db, argv[0], argv[1]), "Failed to add user", false);
        return false;
    }
    
    /* Stickers */
    
    static bool
    sticker_send(struct lkt_state *srv, size_t c, char *name, char *type, int id, int value)
    {
        UNUSED(type);
        struct lkt_message *msg = lkt_message_new();
        msg->data_len = safe_snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s -> %d\n", id, name, value);
        lkt_state_send(srv, c, msg);
        return true;
    }
    
    bool
    command_sticker_create(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        LOG_INFO("COMMAND", "Client %ld is using the sticker create command", c);
        RETURN_UNLESS(argv[0], "Invalid argument", false);
        if (!database_sticker_create(srv->db, argv[0])) {
            LOG_ERROR("COMMAND", "Failed to create sticker '%s'", argv[0]);
            return false;
        }
        LOG_INFO("COMMAND", "Created sticker '%s'", argv[0]);
        return true;
    }
    
    bool
    command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(argv[0] && argv[1] && argv[2] && argv[3] && !argv[4], "Invalid argument", false);
        long uri, value;
        char *endptr, err1, err2;
        STRTOL(uri, argv[1], endptr, err1);
        STRTOL(value, argv[3], endptr, err2);
        RETURN_IF(err1 || err2, "STRTOL failed", false);
        LOG_INFO("COMMAND", "Client %ld is using the sticker set command", c);
        RETURN_UNLESS(database_sticker_set(srv->db, argv[0], argv[2], uri, value),
                      "Can't get sticker", false);
        srv->mpd_idle_events |= MPD_IDLE_STICKER;
        return true;
    }
    
    bool
    command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        LOG_INFO("COMMAND", "Client %ld is using the sticker get command", c);
        struct lkt_search callback = {
            .srv = srv,
            .c = c,
            .call = (void(*)(void)) sticker_send,
            .st_type = argv[0],
        };
    
        /* Simple list {type} {uri} command */
        if (argv[0] != NULL && argv[1] != NULL && argv[2] == NULL) {
            callback.st_uri = atoi(argv[1]);
            if (!database_search_sticker_init(srv->db, &callback))
                return false;
        }
    
        /* list {type} {uri} {name} command */
        else if (argv[0] != NULL && argv[1] != NULL &&
                 argv[2] != NULL && argv[3] == NULL) {
            callback.st_name = argv[2];
            callback.st_uri  = atoi(argv[1]);
            if (!database_search_sticker_init(srv->db, &callback))
                return false;
        }
    
        /* 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) {
            callback.st_name  = argv[2];
            callback.st_uri   = atoi(argv[1]);
            callback.st_op    = argv[3][0];
            callback.st_value = atoi(argv[4]);
            if (!database_search_sticker_init(srv->db, &callback))
                return false;
        }
    
        /* Just list all stickers */
        else if ( (argv[0] != NULL && argv[1] == NULL) || argv[0] == NULL ) {
            if (!database_search_sticker_init(srv->db, &callback))
                return false;
        }
    
        else
            goto unknown;
    
        /* Send results */
        while (database_search_iter(&callback))
            continue;
        return true;
    
    unknown:
        LOG_ERROR("COMMAND", "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])
    {
        LOG_INFO("COMMAND", "Client %ld is using the sticker delete command", c);
        RETURN_UNLESS(argv[0] && argv[1] && argv[2], "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]);
    }
    
    bool
    command_sticker_destroy(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        LOG_INFO("COMMAND", "Client %ld is using the sticker destroy command", c);
        RETURN_UNLESS(argv[0], "Invalid argument", false);
        return database_sticker_delete(srv->db, argv[0]);
    }