Sélectionner une révision Git
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]);
}