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