Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • fa1ddc6514eec2d306b2c5679d7e80134ff109c8
  • 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 42,52 Kio
    #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 <lektor/stb/ds.h>
    #include <poll.h>
    
    bool
    command_restart(struct lkt_state *srv, size_t c, char UNUSED *__argv[LKT_MESSAGE_ARGS_MAX])
    {
        const char *const argv[] = { cmd_get_executable_name(), NULL };
        struct lkt_queue_state sta;
        memset(&sta, 0, sizeof(struct lkt_queue_state));
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
    
        if (!argv[0]) {
            LOG_ERROR("GENERAL", "Can't restart if the executable path was not found at start-up");
            return false;
        }
    
        database_queue_state(srv->db, &sta);
        env_set(LKT_ENV_RESTART, "1");
        size_t len = (size_t)long_length(LONG_MAX);
        if (len > 0) {
            char *pos = LKT_ALLOC_ARRAY(char, len);
            safe_snprintf(pos, len, "%d", sta.current);
            env_set(LKT_ENV_CURRENT, pos);
            free(pos);
        } else
            env_set(LKT_ENV_CURRENT, "NULL");
    
        lkt_close_server(srv);
    
        execv(argv[0], (char *const *)argv);
        LOG_ERROR("GENERAL", "Failed to exec lektor or OS not supported");
        exit(EXIT_FAILURE);
    }
    
    bool
    command_update(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
    {
        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;
    
        struct lkt_uri *user_uri = lkt_uri_new();
        if (argv[0] != NULL)
            lkt_uri_from_list(user_uri, argv);
    
        const bool ret = !MOD_CALL(srv->repo_mod, "update", user_uri);
    
        lkt_uri_free(user_uri);
        return ret;
    }
    
    bool
    command_dry_update(struct lkt_state *srv, size_t c, char UNUSED *argv[LKT_MESSAGE_ARGS_MAX])
    {
        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, "dry-update");
    }
    
    bool
    command_import(struct lkt_state *srv, size_t c, char UNUSED *argv[LKT_MESSAGE_ARGS_MAX])
    {
        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, "import");
    }
    
    bool
    command_rescan(struct lkt_state *srv, size_t c, char UNUSED *argv[LKT_MESSAGE_ARGS_MAX],
                   int UNUSED forced)
    {
        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 UNUSED *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        srv->___signal_INT(srv);
        LOG_FATAL("The SIGINT handler returned, now exiting");
    }
    
    bool
    command_config(struct lkt_state *srv, size_t c, char UNUSED *argv[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
        struct lkt_message *out;
        char db_file[LKT_LINE_MAX];
        memset(db_file, 0, sizeof(db_file));
        database_config_get_text(srv->db, "database", "db_path", db_file, LKT_LINE_MAX);
        out = lkt_message_new_fmt("max_clients: %ld\n"
                                  "db_file: %s\n"
                                  "music_directory: %s\n",
                                  srv->fds_max, db_file, srv->kara_prefix);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_currentsong(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        struct lkt_message *out;
        struct kara_metadata kara;
    
        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_fmt("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);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_stats(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        int artists = 0, albums = 0, songs = 0;
        long ts_update          = 0;
        uint64_t playtime       = 0;
        uint64_t queue_playtime = 0;
    
        database_get_update(srv->db, &ts_update, NULL, NULL);
        database_stats(srv->db, &artists, &albums, &songs);
        database_total_playtime(srv->db, &playtime);
        database_queue_playtime(srv->db, &queue_playtime);
    
        struct lkt_message *out =
            lkt_message_new_fmt("__is_updating: %d\n" /* Custom field here */
                                "__update_progress: %ld:%ld\n"
                                "db_playtime: %ld\n"
                                "playtime: %ld\n"
                                "db_update: %ld\n"
                                "uptime: %ld\n"
                                "artists: %d\n" /* Number of authors */
                                "albums: %d\n"  /* Number of source names */
                                "songs: %d\n",  /* Number of songs */
                                srv->is_updating, srv->update_current, srv->update_total, playtime,
                                queue_playtime, (uint64_t)ts_update,
                                (uint64_t)(time(NULL) - srv->start_date), artists, albums, songs);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_status(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        struct lkt_queue_state queue_state;
        int elapsed, duration, songid = 0;
        long update_job;
        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);
        database_get_update(srv->db, NULL, &update_job, NULL);
    
        play_state = queue_state.current <= 0 ? "stop" : (queue_state.paused ? "pause" : "play");
        struct lkt_message *out =
            lkt_message_new_fmt("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"
                                "updating_db: %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, update_job, songid);
        lkt_state_send(srv, c, out);
        return true;
    }
    
    bool
    command_next(struct lkt_state *srv, UNUSED size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        char filepath[PATH_MAX];
        if (!database_queue_next(srv->db, filepath)) {
            LOG_DEBUG("COMMAND", "Failed to get next, stop the player for end of queue");
            command_stop(srv, c, NULL);
            return false;
        }
        if (MOD_CALL(srv->window_mod, "load", filepath))
            return false;
        else {
            config_handle_hook(srv->db, "kara_load");
            LOG_INFO("COMMAND", "Loaded next kara %s", filepath);
            return true;
        }
    }
    
    bool
    command_pause(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        /* The simple toggle version */
        if ((!args) || (args && !args[0])) {
        toggle_anyway:
            srv->mpd_idle_events |= MPD_IDLE_PLAYER;
            RETURN_UNLESS(database_queue_toggle_pause(srv->db), "Failed to toggle pause in db", false);
            return !MOD_PROC(srv->window_mod, "toggle");
        }
    
        /* Set the 'pause' state */
        else {
            long state = strtol(args[0], NULL, 0);
            struct lkt_queue_state queue;
            LOG_DEBUG("COMMAND", "Forcing the paused state to %ld", state);
            GOTO_UNLESS(database_queue_state(srv->db, &queue),
                        "Failed to get queue state, try to toggle pause state anyway", toggle_anyway);
    
            /* Pause   := 0 | 1
             * current := (kara_id > 0) | (NULL --> -1) */
    
            /* Play! */
            if (queue.current < 0) {
                LOG_DEBUG("COMMAND", "Not playing anything, force play from begening of the queue");
                return command_play(srv, c, NULL);
            }
    
            /* Set play/unpause or the pause */
            else if (queue.paused != state) {
                RETURN_UNLESS(database_queue_set_paused(srv->db, state), "Oupsi, can't get queue state",
                              false);
                srv->mpd_idle_events |= MPD_IDLE_PLAYER;
                return !MOD_PROC(srv->window_mod, "toggle");
            }
    
            /* Nop */
            else {
                LOG_DEBUG("COMMAND", "Playback was already %s", state ? "paused" : "unpaused");
                return true;
            }
        }
    }
    
    bool
    command_previous(struct lkt_state *srv, UNUSED size_t c, char 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");
            LOG_INFO("COMMAND", "Loaded previous kara %s", filepath);
            return true;
        }
    }
    
    PRIVATE_FUNCTION 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, 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_IF(MOD_CALL(win->window_mod, "set_paused", 0), "Can't unset pause", false);
        LOG_DEBUG("COMMAND", "Playing queue at index %d, file %s", pos, filepath);
        return true;
    }
    
    bool
    command_play(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        char *endptr = NULL, err;
        long pos     = 1;
        struct lkt_queue_state queue_state;
    
        /* Argument handle. */
        if (args && args[0]) {
            STRTOL(pos, args[0], endptr, err);
            RETURN_IF(err, "STRTOL failed", false);
        }
    
        /* If position is after the queue, don't do anything */
        RETURN_UNLESS(database_queue_state(srv->db, &queue_state), "Can't get playback status", false);
        if (queue_state.length < pos) {
            LOG_INFO("COMMAND",
                     "Don't perform play command, queue is empty or position is after queue length");
            return 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, (int)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, UNUSED size_t c, 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, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        char *endptr = NULL;
        int pos      = 0;
        char err;
        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, UNUSED size_t c, char 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 = lkt_uri_new();
        RETURN_UNLESS(lkt_uri_from(uri, args), "Failed to parse query", false);
        bool ret = database_queue_add_uri(srv->db, uri, priority);
        lkt_uri_free(uri);
        if (!ret)
            LOG_ERROR("COMMAND", "Failed to add with priority %d in queue", priority);
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return ret;
    }
    
    bool
    command_addid(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args, "Invalid argument", false);
        bool error  = false;
        size_t *ids = NULL;
    
        /* Only one ID */
        if (args[0] && (args[1] == NULL)) {
            long id = strtol(args[0], NULL, 0);
            if (id <= INT_MIN || id >= INT_MAX) {
                LOG_ERROR("COMMAND", "Tried to add a kara with an ID to big! Id was: %zu", id);
                goto end_of_function; /* Use goto here to have a single return statement */
            }
            error = database_queue_add_id(srv->db, (int)id, 1);
        }
    
        /* One ID or more */
        else {
            FOR_EACH_ARGUMENT (i, args) {
                long id = strtol(args[i], NULL, 0);
                if (id < 0) {
                    LOG_ERROR("COMMAND", "Tried to add a kara with a negative ID! Id was: %z", id);
                    goto end_of_function;
                }
                arrput(ids, (size_t)id);
            }
            LOG_DEBUG("COMMAND", "Add a total of %zu IDs to the queue", arrlen(ids));
            error = database_queue_multi_add_id(srv->db, ids, 1);
        }
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
    end_of_function:
        arrfree(ids); /* Handles the `ids == NULL` */
        return error;
    }
    
    bool
    command_clear(struct lkt_state *srv, UNUSED size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return command_stop(srv, c, args) && database_queue_clear(srv->db);
    }
    
    bool
    command_crop(struct lkt_state *srv, UNUSED size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return database_queue_crop(srv->db);
    }
    
    bool
    command_flat(struct lkt_state *srv, UNUSED size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return database_queue_flat(srv->db);
    }
    
    PRIVATE_FUNCTION bool
    skip_current_kara__(struct lkt_state *srv)
    {
        char filepath[PATH_MAX];
    
        if (database_queue_skip_current(srv->db, filepath)) {
            if (MOD_CALL(srv->window_mod, "load", filepath)) {
                LOG_ERROR("COMMAND", "Failed to skip current kara (loading %s)", filepath);
                return false;
            }
            srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        }
    
        else {
            LOG_WARN("COMMAND", "Failed to skip current kara, stop playback");
            MOD_PROC(srv->window_mod, "close");
            return false;
        }
    
        LOG_DEBUG("COMMAND", "Skiped current kara and loaded %s instead", filepath);
        return true;
    }
    
    bool
    command_del(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        struct lkt_queue_state queue = { .current = -2 /* Really need this field only */ };
        long pos                     = -1;
        char *endptr = NULL, err = 0;
        RETURN_UNLESS(args && args[0], "No position provided", false);
    
        STRTOL(pos, args[0], endptr, err);
        RETURN_IF(err, "STRTOL failed", false);
    
        database_queue_state(srv->db, &queue); /* At worst, -1 != -2 */
    
        RETURN_IF(pos == (long)queue.current && !skip_current_kara__(srv),
                  "Failed to skip current kara", false);
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return database_queue_del_pos(srv->db, (int)pos);
    }
    
    bool
    command_delid(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long id;
        char *endptr = NULL, err = 0;
        int uri = 0;
    
        RETURN_UNLESS(args && 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);
        RETURN_IF(id == (long)uri && !skip_current_kara__(srv), "Failed to skip current kara", false);
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return database_queue_del_id(srv->db, (int)id);
    }
    
    bool
    command_move(struct lkt_state UNUSED *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long from, to;
        char *endptr = NULL, 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_PLAYLIST;
        return database_queue_move(srv->db, (int)from, (int)to);
    }
    
    static bool
    __swap_positions(struct lkt_state *srv, int from, int to)
    {
        RETURN_IF(to == from, "Can't swap with two identic positions", false);
    
        struct lkt_queue_state queue = { .current = -1 };
        database_queue_state(srv->db, &queue);
    
        int new_current = (((long)queue.current) == to)     ? from
                          : (((long)queue.current) == from) ? to
                                                            : -1;
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        bool sta = database_queue_swap(srv->db, from, to);
    
        if (sta && (new_current >= 0) && !database_queue_set_current_index(srv->db, new_current)) {
            LOG_ERROR("COMMAND", "Failed to set current index to new location");
            if (!database_queue_swap(srv->db, from, to)) {
                LOG_ERROR("COMMAND", "Reswap failed, uncertain state! Stopping the playback");
                command_stop(srv, 0, NULL);
            }
            return false;
        }
    
        return sta;
    }
    
    bool
    command_swapid(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false);
        int from = database_queue_probe_id(srv->db, (int)strtol(args[0], NULL, 0));
        int to   = database_queue_probe_id(srv->db, (int)strtol(args[1], NULL, 0));
        return __swap_positions(srv, from, to);
    }
    
    bool
    command_swap(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long from, to;
        char *endptr = NULL, err = 0;
    
        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);
    
        return __swap_positions(srv, (int)from, (int)to);
    }
    
    bool
    command_help(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        lkt_state_send(srv, c, lkt_message_new_fmt("HELP\n"));
        return true;
    }
    
    bool
    command_idle(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        bool once;
        size_t i;
    
        for (once = false, i = 0; args[i]; ++i, once |= true) {
            MPD_IDLE_FLAG idle_mask =
                (STR_MATCH(args[i], "database") * MPD_IDLE_DATABASE) +
                (STR_MATCH(args[i], "update") * MPD_IDLE_UPDATE) +
                (STR_MATCH(args[i], "stored_playlist") * MPD_IDLE_STORED_PLAYLIST) +
                (STR_MATCH(args[i], "playlist") * MPD_IDLE_PLAYLIST) +
                (STR_MATCH(args[i], "player") * MPD_IDLE_PLAYER) +
                (STR_MATCH(args[i], "mixer") * MPD_IDLE_MIXER) +
                (STR_MATCH(args[i], "output") * MPD_IDLE_OUTPUT) +
                (STR_MATCH(args[i], "options") * MPD_IDLE_OPTIONS) +
                (STR_MATCH(args[i], "partition") * MPD_IDLE_PARTITION) +
                (STR_MATCH(args[i], "sticker") * MPD_IDLE_STICKER) +
                (STR_MATCH(args[i], "subscription") * MPD_IDLE_SUBSCRIPTION) +
                (STR_MATCH(args[i], "message") * MPD_IDLE_MESSAGE);
            idle_mask = idle_mask + ((unsigned int)(!(bool)(idle_mask)) * MPD_IDLE_ALL);
            lkt_client_add_mask(srv, c, idle_mask);
        }
    
        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;
    }
    
    bool
    command_noidle(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        lkt_client_clear_mask(srv, c);
        return true;
    }
    
    /* Functions for the searchadd and the search mpd commands */
    PRIVATE_FUNCTION 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 =
            lkt_message_new_fmt("%*d: %*d %s\n", pos_len, pos, id_len, id, sql_row);
        lkt_state_send(args->srv, args->c, out);
        return true;
    }
    
    PRIVATE_FUNCTION bool
    lkt_callback_send_row_v2(struct lkt_state *srv, size_t c, int id, int id_len, const char *sql_row)
    {
        lkt_state_send(srv, c, lkt_message_new_fmt("%*d %s\n", id_len, id, sql_row));
        return true;
    }
    
    PRIVATE_FUNCTION bool
    lkt_callback_send_list_plts(struct lkt_state *srv, size_t c, const char *plt_name)
    {
        /* If the playlist is named OK or ACK... */
        lkt_state_send(srv, c, lkt_message_new_fmt("name: %s\n", plt_name));
        return true;
    }
    
    static bool
    command_findid(struct lkt_state *srv, size_t c, char *id_str)
    {
        uint64_t kara_duration;
        struct kara_metadata kara;
        char filepath[PATH_MAX] = { 0 };
        long id                 = strtol(id_str, NULL, 0); /* XXX: Must be valid here */
        int s = 0, h = 0, m = 0;
    
        memset(&kara, 0, sizeof(kara));
    
        if (!database_kara_by_id(srv->db, (int)id, &kara, filepath)) {
            LOG_ERROR("COMMAND", "Can't get a kara with id %ld", id);
            return false;
        }
    
        if (!database_get_kara_mtime_id(srv->db, (int)id, &kara_duration)) {
            LOG_WARN("COMMAND", "Can't get duration for kara %ld", id);
            kara_duration = 0;
        }
    
        if (kara_duration < (uint64_t)INT_MAX) {
            s = (int)kara_duration;
            h = s / 3600;
            s = s % 3600;
            m = s / 60;
            s = s % 60;
        }
    
        static const char *send_format = "Title: %s\n"
                                         "Author: %s\n"
                                         "Source: %s\n"
                                         "Type: %s%d\n"
                                         "Category: %s\n"
                                         "Language: %s\n"
                                         "Duration: %d:%02d:%02d\n";
        struct lkt_message *out;
        out = lkt_message_new_fmt(send_format, 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;
    }
    
    PRIVATE_FUNCTION bool
    iter_search__(struct lkt_search *search)
    {
        size_t count;
        for (count = 0; database_search_iter(search); ++count)
            continue;
    
        if (count) {
            struct lkt_state *srv = database_search_get_srv(search);
            size_t client         = database_search_get_client(search);
            size_t continuation   = database_search_get_continuation(search);
            lkt_set_continuation(srv, client, (int)(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,
                 database_search_init_func init)
    {
        RETURN_UNLESS(args[0], "Invalid argument", false);
    
        /* Just an id */
        long count;
        if (!args[1] && (count = strtol(args[0], NULL, 0)))
            return command_findid(srv, c, args[0]);
    
        /* With an URI */
        struct lkt_uri *find_uri = lkt_uri_new();
        struct lkt_search *search =
            database_search_new(srv, c, (int)continuation, FUNCTION_POINTER(lkt_callback_send_row_v2));
        database_search_set_name(search, args[0]);
        database_search_set_uri(search, find_uri);
    
        if (!lkt_uri_from(find_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]);
            unless (lkt_uri_from(find_uri, &args[1])) {
                lkt_uri_free(find_uri);
                LOG_ERROR("COMMAND", "Failed to create the uri");
                return false;
            }
        }
    
        unless (init(srv->db, search)) {
            LOG_ERROR("COMMAND", "Failed to init the search structure");
            lkt_uri_free(find_uri);
            return false;
        }
    
        const bool ret = iter_search__(search);
        lkt_uri_free(find_uri);
        return ret;
    }
    
    bool
    command_plt_list(struct lkt_state *srv, size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX], int cont)
    {
        struct lkt_search *search =
            database_search_new(srv, c, cont, FUNCTION_POINTER(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_uri *null_uri = lkt_uri_new();
        struct lkt_search *search =
            database_search_new(srv, c, cont, FUNCTION_POINTER(lkt_callback_send_row_v2));
        database_search_set_uri(search, null_uri);
        database_search_set_name(search, args[0]);
    
        RETURN_UNLESS(database_search_playlist_init(srv->db, search), "Failed to init search", false);
        const bool ret = iter_search__(search);
        lkt_uri_free(null_uri);
        return ret;
    }
    
    bool
    command_set_playback_option(struct lkt_state *srv, size_t UNUSED c, LKT_PLAYBACK_OPTION opt,
                                char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(srv, "Invalid argument", false);
        long val;
        char *endptr = NULL;
        bool 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;
        }
    
        const int val_int = (int)val;
    
        switch (opt) {
        case LKT_PLAYBACK_OPTION_RANDOM: ret = database_config_queue(srv->db, "random", val_int); break;
        case LKT_PLAYBACK_OPTION_SINGLE: ret = database_config_queue(srv->db, "single", val_int); break;
        case LKT_PLAYBACK_OPTION_CONSUME:
            ret = database_config_queue(srv->db, "consume", val_int);
            break;
        case LKT_PLAYBACK_OPTION_REPEAT: ret = database_config_queue(srv->db, "repeat", val_int); break;
    
        case LKT_PLAYBACK_OPTION_VOLUME:
            ret = database_config_queue(srv->db, "volume", val_int);
            LOG_INFO("COMMAND", "Set volume to %ld", val_int);
            ret &= !MOD_CALL(srv->window_mod, "set_volume", val_int);
            srv->mpd_idle_events |= MPD_IDLE_MIXER;
            break;
    
        case LKT_PLAYBACK_OPTION_NONE: 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_PLAYLIST;
        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_seek(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long pos, seconds, err_flag = 0;
        char *endptr = NULL;
        RETURN_UNLESS(args && args[0] && args[1], "Invalid arguments", false);
    
        STRTOL(pos, args[0], endptr, err_flag);
        RETURN_IF(err_flag, "Failed to get a number!", false);
    
        STRTOL(seconds, args[1], endptr, err_flag);
        RETURN_IF(err_flag, "Failed to get a number!", false);
    
        RETURN_UNLESS(command_set_pos(srv, (int)pos), "Failed to get to the right file", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return !MOD_CALL(srv->window_mod, "set_position", seconds);
    }
    
    bool
    command_seekid(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long id, seconds, err_flag = 0;
        char *endptr = NULL;
        int position;
        RETURN_UNLESS(args && args[0] && args[1], "Invalid arguments", false);
    
        STRTOL(id, args[0], endptr, err_flag);
        RETURN_IF(err_flag, "Failed to get a number!", false);
    
        STRTOL(seconds, args[1], endptr, err_flag);
        RETURN_IF(err_flag, "Failed to get a number!", false);
    
        RETURN_UNLESS(database_get_kara_position(srv->db, (int)id, &position),
                      "Can't find kara in queue", false);
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        RETURN_UNLESS(command_set_pos(srv, (int)position), "Failed to set position to right kara",
                      false);
        return !MOD_CALL(srv->window_mod, "set_position", (int)seconds);
    }
    
    bool
    command_seekcur(struct lkt_state *srv, UNUSED size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        long seconds, err_flag = 0;
        char *endptr = NULL;
        RETURN_UNLESS(args && args[0], "Invalid arguments", false);
    
        STRTOL(seconds, args[0], endptr, err_flag);
        RETURN_IF(err_flag, "Failed to get a number!", false);
    
        srv->mpd_idle_events |= MPD_IDLE_PLAYER;
        return !MOD_CALL(srv->window_mod, "set_position", (int)seconds);
    }
    
    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;
    
        if (args[1] == NULL)
            ret = database_plt_create(srv->db, args[0]);
    
        else {
            struct lkt_uri *uri = lkt_uri_new();
            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_STORED_PLAYLIST;
    
        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 = NULL, err;
        long pos;
    
        if (args[1] == NULL) {
            srv->mpd_idle_events |= MPD_IDLE_STORED_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_STORED_PLAYLIST;
        return database_plt_remove_pos(srv->db, args[0], (int)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_STORED_PLAYLIST;
        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_STORED_PLAYLIST;
        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_STORED_PLAYLIST;
        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_STORED_PLAYLIST;
        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 = lkt_uri_new();
        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_STORED_PLAYLIST;
        return true;
    }
    
    bool
    command_shuffle(struct lkt_state *srv, UNUSED size_t c, char UNUSED *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(database_queue_shuffle(srv->db), "Failed to shuffle", false);
        srv->mpd_idle_events |= MPD_IDLE_PLAYLIST;
        return true;
    }
    
    bool
    command_queue_listid(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        RETURN_UNLESS(args && args[0], "Invalid arguments", false);
        long id = strtol(args[0], NULL, 0);
        return database_queue_probe_id(srv->db, (int)id) && command_findid(srv, c, args[0]);
    }
    
    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                 = NULL, 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 = (unsigned int)labs(val);
    
        /* There is nothing after, is is the song pos version. */
        if (endptr && (*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 = (unsigned int)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 + (unsigned int)lkt_remaining_msg(srv, c) - 3;
            lkt_set_continuation(srv, c, (int)(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 */
    
    // clang-format off
    PRIVATE_FUNCTION bool command_sticker_create(struct lkt_state *, size_t, char *[LKT_MESSAGE_ARGS_MAX - 1]);
    PRIVATE_FUNCTION bool command_sticker_get   (struct lkt_state *, size_t, char *[LKT_MESSAGE_ARGS_MAX - 1]);
    PRIVATE_FUNCTION bool command_sticker_set   (struct lkt_state *, size_t, char *[LKT_MESSAGE_ARGS_MAX - 1]);
    PRIVATE_FUNCTION bool command_sticker_delete(struct lkt_state *, size_t, char *[LKT_MESSAGE_ARGS_MAX - 1]);
    // clang-format on
    
    PRIVATE_FUNCTION bool
    sticker_send(struct lkt_state *srv, size_t c, char *name, char UNUSED *type, int id, int value)
    {
        lkt_state_send(srv, c, lkt_message_new_fmt("%d: %s -> %d\n", id, name, value));
        return true;
    }
    
    PRIVATE_FUNCTION bool
    command_sticker_null(struct lkt_state UNUSED *srv, size_t UNUSED c,
                         char UNUSED *args[LKT_MESSAGE_ARGS_MAX - 1])
    {
        return false;
    }
    
    bool
    command_sticker_handle(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX])
    {
        typedef enum {
            LKT_STICKER_HANDLER_NULL  = 0,
            LKT_STICKER_HANDLER_GET   = 1,
            LKT_STICKER_HANDLER_SET   = 2,
            LKT_STICKER_HANDLER_DEL   = 3,
            LKT_STICKER_HANDLER_CRATE = 4,
        } LKT_STICKER_HANDLER_TYPE;
    
        typedef bool (*handler_func)(struct lkt_state *, size_t, char * [LKT_MESSAGE_ARGS_MAX - 1]);
    
        static const handler_func handler_lookup[] = {
            command_sticker_null,   command_sticker_get,    command_sticker_set,
            command_sticker_delete, command_sticker_create,
        };
    
        LKT_STICKER_HANDLER_TYPE handler_type =
            ((STR_MATCH(args[0], "get") || STR_MATCH(args[0], "list") || STR_MATCH(args[0], "find")) *
             LKT_STICKER_HANDLER_GET) +
            ((STR_MATCH(args[0], "set")) * LKT_STICKER_HANDLER_SET) +
            ((STR_MATCH(args[0], "delete")) * LKT_STICKER_HANDLER_DEL) +
            ((STR_MATCH(args[0], "__create")) * LKT_STICKER_HANDLER_CRATE);
    
        return handler_lookup[handler_type](srv, c, args + 1);
    }
    
    PRIVATE_FUNCTION bool
    command_sticker_create(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX - 1])
    {
        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;
    }
    
    PRIVATE_FUNCTION bool
    command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX - 1])
    {
        RETURN_UNLESS(argv[0] && argv[1] && argv[2] && argv[3] && !argv[4], "Invalid argument", false);
        long uri, value;
        char *endptr = NULL, 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], (int)uri, (int)value),
                      "Can't get sticker", false);
        srv->mpd_idle_events |= MPD_IDLE_STICKER;
        return true;
    }
    
    PRIVATE_FUNCTION bool
    command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX - 1])
    {
        LOG_INFO("COMMAND", "Client %ld is using the sticker get command", c);
        struct lkt_sticker_opt stkr = LKT_STICKER_OPT_INIT;
        struct lkt_search *callback = database_search_new(srv, c, 0, FUNCTION_POINTER(sticker_send));
        database_search_set_name(callback, argv[0]);
    
        /* Simple list {type} {uri} command */
        if (argv[0] != NULL && argv[1] != NULL && argv[2] == NULL) {
            stkr.uri = atoi(argv[1]);
            database_search_set_sticker(callback, &stkr);
            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) {
            stkr.name = argv[2];
            stkr.uri  = atoi(argv[1]);
            database_search_set_sticker(callback, &stkr);
            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) {
            stkr.name  = argv[2];
            stkr.uri   = atoi(argv[1]);
            stkr.op    = argv[3][0];
            stkr.value = atoi(argv[4]);
            database_search_set_sticker(callback, &stkr);
            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;
    }
    
    PRIVATE_FUNCTION bool
    command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX - 1])
    {
        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]);
    }
    
    PRIVATE_FUNCTION 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]);
    }