aboutsummaryrefslogtreecommitdiffstats
path: root/src/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/command.c')
-rw-r--r--src/command.c1038
1 files changed, 579 insertions, 459 deletions
diff --git a/src/command.c b/src/command.c
index 64f161805..7d4403399 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,17 +19,19 @@
#include "config.h"
#include "command.h"
+#include "protocol/argparser.h"
+#include "protocol/result.h"
#include "player_control.h"
#include "playlist.h"
#include "playlist_print.h"
#include "playlist_save.h"
#include "playlist_queue.h"
+#include "playlist_error.h"
#include "queue_print.h"
#include "ls.h"
#include "uri.h"
#include "decoder_print.h"
#include "directory.h"
-#include "directory_print.h"
#include "database.h"
#include "update.h"
#include "volume.h"
@@ -42,18 +44,28 @@
#include "output_print.h"
#include "locate.h"
#include "dbUtils.h"
+#include "db_error.h"
+#include "db_print.h"
+#include "db_selection.h"
+#include "db_lock.h"
#include "tag.h"
#include "client.h"
+#include "client_idle.h"
+#include "client_internal.h"
+#include "client_subscribe.h"
+#include "client_file.h"
#include "tag_print.h"
#include "path.h"
#include "replay_gain_config.h"
#include "idle.h"
+#include "mapper.h"
+#include "song.h"
+#include "song_print.h"
#ifdef ENABLE_SQLITE
#include "sticker.h"
#include "sticker_print.h"
#include "song_sticker.h"
-#include "song_print.h"
#endif
#include <assert.h>
@@ -98,221 +110,6 @@ struct command {
enum command_return (*handler)(struct client *client, int argc, char **argv);
};
-/* this should really be "need a non-negative integer": */
-static const char need_positive[] = "need a positive integer"; /* no-op */
-static const char need_range[] = "need a range";
-
-/* FIXME: redundant error messages */
-static const char check_integer[] = "\"%s\" is not a integer";
-static const char need_integer[] = "need an integer";
-
-static const char *current_command;
-static int command_list_num;
-
-void command_success(struct client *client)
-{
- client_puts(client, "OK\n");
-}
-
-static void command_error_v(struct client *client, enum ack error,
- const char *fmt, va_list args)
-{
- assert(client != NULL);
- assert(current_command != NULL);
-
- client_printf(client, "ACK [%i@%i] {%s} ",
- (int)error, command_list_num, current_command);
- client_vprintf(client, fmt, args);
- client_puts(client, "\n");
-
- current_command = NULL;
-}
-
-G_GNUC_PRINTF(3, 4) static void command_error(struct client *client, enum ack error,
- const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- command_error_v(client, error, fmt, args);
- va_end(args);
-}
-
-static bool G_GNUC_PRINTF(4, 5)
-check_uint32(struct client *client, uint32_t *dst,
- const char *s, const char *fmt, ...)
-{
- char *test;
-
- *dst = strtoul(s, &test, 10);
- if (*test != '\0') {
- va_list args;
- va_start(args, fmt);
- command_error_v(client, ACK_ERROR_ARG, fmt, args);
- va_end(args);
- return false;
- }
- return true;
-}
-
-static bool G_GNUC_PRINTF(4, 5)
-check_int(struct client *client, int *value_r,
- const char *s, const char *fmt, ...)
-{
- char *test;
- long value;
-
- value = strtol(s, &test, 10);
- if (*test != '\0') {
- va_list args;
- va_start(args, fmt);
- command_error_v(client, ACK_ERROR_ARG, fmt, args);
- va_end(args);
- return false;
- }
-
-#if G_MAXLONG > G_MAXINT
- if (value < G_MININT || value > G_MAXINT) {
- command_error(client, ACK_ERROR_ARG,
- "Number too large: %s", s);
- return false;
- }
-#endif
-
- *value_r = (int)value;
- return true;
-}
-
-static bool G_GNUC_PRINTF(5, 6)
-check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
- const char *s, const char *fmt, ...)
-{
- char *test, *test2;
- long value;
-
- value = strtol(s, &test, 10);
- if (*test != '\0' && *test != ':') {
- va_list args;
- va_start(args, fmt);
- command_error_v(client, ACK_ERROR_ARG, fmt, args);
- va_end(args);
- return false;
- }
-
- if (value == -1 && *test == 0) {
- /* compatibility with older MPD versions: specifying
- "-1" makes MPD display the whole list */
- *value_r1 = 0;
- *value_r2 = G_MAXUINT;
- return true;
- }
-
- if (value < 0) {
- command_error(client, ACK_ERROR_ARG,
- "Number is negative: %s", s);
- return false;
- }
-
-#if G_MAXLONG > G_MAXUINT
- if (value > G_MAXUINT) {
- command_error(client, ACK_ERROR_ARG,
- "Number too large: %s", s);
- return false;
- }
-#endif
-
- *value_r1 = (unsigned)value;
-
- if (*test == ':') {
- value = strtol(++test, &test2, 10);
- if (*test2 != '\0') {
- va_list args;
- va_start(args, fmt);
- command_error_v(client, ACK_ERROR_ARG, fmt, args);
- va_end(args);
- return false;
- }
-
- if (test == test2)
- value = G_MAXUINT;
-
- if (value < 0) {
- command_error(client, ACK_ERROR_ARG,
- "Number is negative: %s", s);
- return false;
- }
-
-#if G_MAXLONG > G_MAXUINT
- if (value > G_MAXUINT) {
- command_error(client, ACK_ERROR_ARG,
- "Number too large: %s", s);
- return false;
- }
-#endif
- *value_r2 = (unsigned)value;
- } else {
- *value_r2 = (unsigned)value + 1;
- }
-
- return true;
-}
-
-static bool
-check_unsigned(struct client *client, unsigned *value_r, const char *s)
-{
- unsigned long value;
- char *endptr;
-
- value = strtoul(s, &endptr, 10);
- if (*endptr != 0) {
- command_error(client, ACK_ERROR_ARG,
- "Integer expected: %s", s);
- return false;
- }
-
- if (value > G_MAXUINT) {
- command_error(client, ACK_ERROR_ARG,
- "Number too large: %s", s);
- return false;
- }
-
- *value_r = (unsigned)value;
- return true;
-}
-
-static bool
-check_bool(struct client *client, bool *value_r, const char *s)
-{
- long value;
- char *endptr;
-
- value = strtol(s, &endptr, 10);
- if (*endptr != 0 || (value != 0 && value != 1)) {
- command_error(client, ACK_ERROR_ARG,
- "Boolean (0/1) expected: %s", s);
- return false;
- }
-
- *value_r = !!value;
- return true;
-}
-
-static bool
-check_float(struct client *client, float *value_r, const char *s)
-{
- float value;
- char *endptr;
-
- value = strtof(s, &endptr);
- if (*endptr != 0 && endptr == s) {
- command_error(client, ACK_ERROR_ARG,
- "Float expected: %s", s);
- return false;
- }
-
- *value_r = value;
- return true;
-}
-
static enum command_return
print_playlist_result(struct client *client,
enum playlist_result result)
@@ -322,11 +119,12 @@ print_playlist_result(struct client *client,
return COMMAND_RETURN_OK;
case PLAYLIST_RESULT_ERRNO:
- command_error(client, ACK_ERROR_SYSTEM, "%s", strerror(errno));
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(errno));
return COMMAND_RETURN_ERROR;
case PLAYLIST_RESULT_DENIED:
- command_error(client, ACK_ERROR_NO_EXIST, "Access denied");
+ command_error(client, ACK_ERROR_PERMISSION, "Access denied");
return COMMAND_RETURN_ERROR;
case PLAYLIST_RESULT_NO_SUCH_SONG:
@@ -372,6 +170,50 @@ print_playlist_result(struct client *client,
return COMMAND_RETURN_ERROR;
}
+/**
+ * Send the GError to the client and free the GError.
+ */
+static enum command_return
+print_error(struct client *client, GError *error)
+{
+ assert(client != NULL);
+ assert(error != NULL);
+
+ g_warning("%s", error->message);
+
+ if (error->domain == playlist_quark()) {
+ enum playlist_result result = error->code;
+ g_error_free(error);
+ return print_playlist_result(client, result);
+ } else if (error->domain == ack_quark()) {
+ command_error(client, error->code, "%s", error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ } else if (error->domain == db_quark()) {
+ switch ((enum db_error)error->code) {
+ case DB_DISABLED:
+ command_error(client, ACK_ERROR_NO_EXIST, "%s",
+ error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+
+ case DB_NOT_FOUND:
+ g_error_free(error);
+ command_error(client, ACK_ERROR_NO_EXIST, "Not found");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else if (error->domain == g_file_error_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(error->code));
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ g_error_free(error);
+ command_error(client, ACK_ERROR_UNKNOWN, "error");
+ return COMMAND_RETURN_ERROR;
+}
+
static void
print_spl_list(struct client *client, GPtrArray *list)
{
@@ -404,7 +246,7 @@ static enum command_return
handle_urlhandlers(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- if (client_get_uid(client) > 0)
+ if (client_is_local(client))
client_puts(client, "handler: file://\n");
print_supported_uri_schemes(client);
return COMMAND_RETURN_OK;
@@ -432,9 +274,9 @@ handle_play(struct client *client, int argc, char *argv[])
int song = -1;
enum playlist_result result;
- if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
+ if (argc == 2 && !check_int(client, &song, argv[1]))
return COMMAND_RETURN_ERROR;
- result = playlist_play(&g_playlist, song);
+ result = playlist_play(&g_playlist, client->player_control, song);
return print_playlist_result(client, result);
}
@@ -444,10 +286,10 @@ handle_playid(struct client *client, int argc, char *argv[])
int id = -1;
enum playlist_result result;
- if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
+ if (argc == 2 && !check_int(client, &id, argv[1]))
return COMMAND_RETURN_ERROR;
- result = playlist_play_id(&g_playlist, id);
+ result = playlist_play_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result);
}
@@ -455,7 +297,7 @@ static enum command_return
handle_stop(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_stop(&g_playlist);
+ playlist_stop(&g_playlist, client->player_control);
return COMMAND_RETURN_OK;
}
@@ -476,9 +318,9 @@ handle_pause(struct client *client,
if (!check_bool(client, &pause_flag, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_pause(pause_flag);
+ pc_set_pause(client->player_control, pause_flag);
} else
- pc_pause();
+ pc_pause(client->player_control);
return COMMAND_RETURN_OK;
}
@@ -493,7 +335,7 @@ handle_status(struct client *client,
char *error;
int song;
- pc_get_status(&player_status);
+ pc_get_status(client->player_control, &player_status);
switch (player_status.state) {
case PLAYER_STATE_STOP:
@@ -526,9 +368,9 @@ handle_status(struct client *client,
playlist_get_consume(&g_playlist),
playlist_get_version(&g_playlist),
playlist_get_length(&g_playlist),
- (int)(pc_get_cross_fade() + 0.5),
- pc_get_mixramp_db(),
- pc_get_mixramp_delay(),
+ (int)(pc_get_cross_fade(client->player_control) + 0.5),
+ pc_get_mixramp_db(client->player_control),
+ pc_get_mixramp_delay(client->player_control),
state);
song = playlist_get_current_song(&g_playlist);
@@ -561,7 +403,7 @@ handle_status(struct client *client,
updateJobId);
}
- error = pc_get_error_message();
+ error = pc_get_error_message(client->player_control);
if (error != NULL) {
client_printf(client,
COMMAND_STATUS_ERROR ": %s\n",
@@ -601,13 +443,16 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
enum playlist_result result;
if (strncmp(uri, "file:///", 8) == 0) {
-#ifdef WIN32
- result = PLAYLIST_RESULT_DENIED;
-#else
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
result = playlist_append_file(&g_playlist,
- uri + 7, client_get_uid(client),
+ client->player_control,
+ path,
NULL);
-#endif
return print_playlist_result(client, result);
}
@@ -618,18 +463,16 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = playlist_append_uri(&g_playlist, uri, NULL);
+ result = playlist_append_uri(&g_playlist,
+ client->player_control,
+ uri, NULL);
return print_playlist_result(client, result);
}
- result = addAllIn(uri);
- if (result == (enum playlist_result)-1) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
- return COMMAND_RETURN_ERROR;
- }
-
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return addAllIn(client->player_control, uri, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -640,13 +483,16 @@ handle_addid(struct client *client, int argc, char *argv[])
enum playlist_result result;
if (strncmp(uri, "file:///", 8) == 0) {
-#ifdef WIN32
- result = PLAYLIST_RESULT_DENIED;
-#else
- result = playlist_append_file(&g_playlist, uri + 7,
- client_get_uid(client),
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ result = playlist_append_file(&g_playlist,
+ client->player_control,
+ path,
&added_id);
-#endif
} else {
if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
@@ -654,21 +500,25 @@ handle_addid(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = playlist_append_uri(&g_playlist, uri, &added_id);
+ result = playlist_append_uri(&g_playlist,
+ client->player_control,
+ uri, &added_id);
}
if (result != PLAYLIST_RESULT_SUCCESS)
return print_playlist_result(client, result);
if (argc == 3) {
- int to;
- if (!check_int(client, &to, argv[2], check_integer, argv[2]))
+ unsigned to;
+ if (!check_unsigned(client, &to, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, added_id, to);
+ result = playlist_move_id(&g_playlist, client->player_control,
+ added_id, to);
if (result != PLAYLIST_RESULT_SUCCESS) {
enum command_return ret =
print_playlist_result(client, result);
- playlist_delete_id(&g_playlist, added_id);
+ playlist_delete_id(&g_playlist, client->player_control,
+ added_id);
return ret;
}
}
@@ -683,23 +533,24 @@ handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
unsigned start, end;
enum playlist_result result;
- if (!check_range(client, &start, &end, argv[1], need_range))
+ if (!check_range(client, &start, &end, argv[1]))
return COMMAND_RETURN_ERROR;
- result = playlist_delete_range(&g_playlist, start, end);
+ result = playlist_delete_range(&g_playlist, client->player_control,
+ start, end);
return print_playlist_result(client, result);
}
static enum command_return
handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int id;
+ unsigned id;
enum playlist_result result;
- if (!check_int(client, &id, argv[1], need_positive))
+ if (!check_unsigned(client, &id, argv[1]))
return COMMAND_RETURN_ERROR;
- result = playlist_delete_id(&g_playlist, id);
+ result = playlist_delete_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result);
}
@@ -716,11 +567,10 @@ handle_shuffle(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
unsigned start = 0, end = queue_length(&g_playlist.queue);
- if (argc == 2 && !check_range(client, &start, &end,
- argv[1], need_range))
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
return COMMAND_RETURN_ERROR;
- playlist_shuffle(&g_playlist, start, end);
+ playlist_shuffle(&g_playlist, client->player_control, start, end);
return COMMAND_RETURN_OK;
}
@@ -728,7 +578,7 @@ static enum command_return
handle_clear(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_clear(&g_playlist);
+ playlist_clear(&g_playlist, client->player_control);
return COMMAND_RETURN_OK;
}
@@ -743,16 +593,40 @@ handle_save(struct client *client,
}
static enum command_return
-handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+handle_load(struct client *client, int argc, char *argv[])
{
+ unsigned start_index, end_index;
+
+ if (argc < 3) {
+ start_index = 0;
+ end_index = G_MAXUINT;
+ } else if (!check_range(client, &start_index, &end_index, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
enum playlist_result result;
- result = playlist_open_into_queue(argv[1], &g_playlist);
+ result = playlist_open_into_queue(argv[1],
+ start_index, end_index,
+ &g_playlist,
+ client->player_control, true);
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
return print_playlist_result(client, result);
- result = playlist_load_spl(&g_playlist, argv[1]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ if (playlist_load_spl(&g_playlist, client->player_control,
+ argv[1], start_index, end_index,
+ &error))
+ return COMMAND_RETURN_OK;
+
+ if (error->domain == playlist_quark() &&
+ error->code == PLAYLIST_RESULT_BAD_NAME)
+ /* the message for BAD_NAME is confusing when the
+ client wants to load a playlist file from the music
+ directory; patch the GError object to show "no such
+ playlist" instead */
+ error->code = PLAYLIST_RESULT_NO_SUCH_LIST;
+
+ return print_error(client, error);
}
static enum command_return
@@ -761,15 +635,10 @@ handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (playlist_file_print(client, argv[1], false))
return COMMAND_RETURN_OK;
- bool ret;
-
- ret = spl_print(client, argv[1], false);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
+ GError *error = NULL;
+ return spl_print(client, argv[1], false, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -779,22 +648,16 @@ handle_listplaylistinfo(struct client *client,
if (playlist_file_print(client, argv[1], true))
return COMMAND_RETURN_OK;
- bool ret;
-
- ret = spl_print(client, argv[1], true);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
+ GError *error = NULL;
+ return spl_print(client, argv[1], true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_lsinfo(struct client *client, int argc, char *argv[])
{
const char *uri;
- const struct directory *directory;
if (argc == 2)
uri = argv[1];
@@ -802,17 +665,35 @@ handle_lsinfo(struct client *client, int argc, char *argv[])
/* default is root directory */
uri = "";
- directory = db_get_directory(uri);
- if (directory == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory not found");
- return COMMAND_RETURN_ERROR;
+ if (strncmp(uri, "file:///", 8) == 0) {
+ /* print information about an arbitrary local file */
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ struct song *song = song_file_load(path, NULL);
+ if (song == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such file");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ song_print_info(client, song);
+ song_free(song);
+ return COMMAND_RETURN_OK;
}
- directory_print(client, directory);
+ struct db_selection selection;
+ db_selection_init(&selection, uri, false);
+
+ GError *error = NULL;
+ if (!db_selection_print(client, &selection, true, &error))
+ return print_error(client, error);
if (isRootDirectory(uri)) {
- GPtrArray *list = spl_list();
+ GPtrArray *list = spl_list(NULL);
if (list != NULL) {
print_spl_list(client, list);
spl_list_free(list);
@@ -825,19 +706,19 @@ handle_lsinfo(struct client *client, int argc, char *argv[])
static enum command_return
handle_rm(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- enum playlist_result result;
-
- result = spl_delete(argv[1]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_delete(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_rename(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- enum playlist_result result;
-
- result = spl_rename(argv[1], argv[2]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_rename(argv[1], argv[2], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -845,7 +726,7 @@ handle_plchanges(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
uint32_t version;
- if (!check_uint32(client, &version, argv[1], need_positive))
+ if (!check_uint32(client, &version, argv[1]))
return COMMAND_RETURN_ERROR;
playlist_print_changes_info(client, &g_playlist, version);
@@ -857,7 +738,7 @@ handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[
{
uint32_t version;
- if (!check_uint32(client, &version, argv[1], need_positive))
+ if (!check_uint32(client, &version, argv[1]))
return COMMAND_RETURN_ERROR;
playlist_print_changes_position(client, &g_playlist, version);
@@ -870,8 +751,7 @@ handle_playlistinfo(struct client *client, int argc, char *argv[])
unsigned start = 0, end = G_MAXUINT;
bool ret;
- if (argc == 2 && !check_range(client, &start, &end,
- argv[1], need_range))
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
return COMMAND_RETURN_ERROR;
ret = playlist_print_info(client, &g_playlist, start, end);
@@ -885,12 +765,11 @@ handle_playlistinfo(struct client *client, int argc, char *argv[])
static enum command_return
handle_playlistid(struct client *client, int argc, char *argv[])
{
- int id = -1;
-
- if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
- return COMMAND_RETURN_ERROR;
+ if (argc >= 2) {
+ unsigned id;
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
- if (id >= 0) {
bool ret = playlist_print_id(client, &g_playlist, id);
if (!ret)
return print_playlist_result(client,
@@ -905,7 +784,6 @@ handle_playlistid(struct client *client, int argc, char *argv[])
static enum command_return
handle_find(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -917,10 +795,10 @@ handle_find(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = findSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret = findSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -930,7 +808,6 @@ handle_find(struct client *client, int argc, char *argv[])
static enum command_return
handle_findadd(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
if (list == NULL || list->length == 0) {
@@ -941,10 +818,11 @@ handle_findadd(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = findAddIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret =
+ findAddIn(client->player_control, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -954,7 +832,6 @@ handle_findadd(struct client *client, int argc, char *argv[])
static enum command_return
handle_search(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -966,10 +843,10 @@ handle_search(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = searchForSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret = searchForSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -979,7 +856,6 @@ handle_search(struct client *client, int argc, char *argv[])
static enum command_return
handle_count(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -991,10 +867,11 @@ handle_count(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = searchStatsForSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret =
+ searchStatsForSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -1047,30 +924,32 @@ static enum command_return
handle_playlistdelete(struct client *client,
G_GNUC_UNUSED int argc, char *argv[]) {
char *playlist = argv[1];
- int from;
- enum playlist_result result;
+ unsigned from;
- if (!check_int(client, &from, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &from, argv[2]))
return COMMAND_RETURN_ERROR;
- result = spl_remove_index(playlist, from);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_remove_index(playlist, from, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *playlist = argv[1];
- int from, to;
- enum playlist_result result;
+ unsigned from, to;
- if (!check_int(client, &from, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &from, argv[2]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[3], check_integer, argv[3]))
+ if (!check_unsigned(client, &to, argv[3]))
return COMMAND_RETURN_ERROR;
- result = spl_move_index(playlist, from, to);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_move_index(playlist, from, to, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1141,7 +1020,7 @@ handle_next(G_GNUC_UNUSED struct client *client,
int single = g_playlist.queue.single;
g_playlist.queue.single = false;
- playlist_next(&g_playlist);
+ playlist_next(&g_playlist, client->player_control);
g_playlist.queue.single = single;
return COMMAND_RETURN_OK;
@@ -1151,37 +1030,96 @@ static enum command_return
handle_previous(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_previous(&g_playlist);
+ playlist_previous(&g_playlist, client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_prio(struct client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned start_position, end_position;
+ if (!check_range(client, &start_position, &end_position,
+ argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority(&g_playlist,
+ client->player_control,
+ start_position, end_position,
+ priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_prioid(struct client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned song_id;
+ if (!check_unsigned(client, &song_id, argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority_id(&g_playlist,
+ client->player_control,
+ song_id, priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
return COMMAND_RETURN_OK;
}
static enum command_return
handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- char *directory = NULL;
- int ret;
+ const char *directory = "";
if (argc == 2)
directory = argv[1];
- ret = printAllIn(client, directory);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
- return ret;
+ GError *error = NULL;
+ return printAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int level;
+ unsigned level;
bool success;
- if (!check_int(client, &level, argv[1], need_integer))
+ if (!check_unsigned(client, &level, argv[1]))
return COMMAND_RETURN_ERROR;
- if (level < 0 || level > 100) {
+ if (level > 100) {
command_error(client, ACK_ERROR_ARG, "Invalid volume value");
return COMMAND_RETURN_ERROR;
}
@@ -1199,52 +1137,31 @@ handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
static enum command_return
handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int status;
-
- if (!check_int(client, &status, argv[1], need_integer))
- return COMMAND_RETURN_ERROR;
-
- if (status != 0 && status != 1) {
- command_error(client, ACK_ERROR_ARG,
- "\"%i\" is not 0 or 1", status);
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
return COMMAND_RETURN_ERROR;
- }
- playlist_set_repeat(&g_playlist, status);
+ playlist_set_repeat(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
static enum command_return
handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int status;
-
- if (!check_int(client, &status, argv[1], need_integer))
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
return COMMAND_RETURN_ERROR;
- if (status != 0 && status != 1) {
- command_error(client, ACK_ERROR_ARG,
- "\"%i\" is not 0 or 1", status);
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_set_single(&g_playlist, status);
+ playlist_set_single(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
static enum command_return
handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int status;
-
- if (!check_int(client, &status, argv[1], need_integer))
- return COMMAND_RETURN_ERROR;
-
- if (status != 0 && status != 1) {
- command_error(client, ACK_ERROR_ARG,
- "\"%i\" is not 0 or 1", status);
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
return COMMAND_RETURN_ERROR;
- }
playlist_set_consume(&g_playlist, status);
return COMMAND_RETURN_OK;
@@ -1253,18 +1170,11 @@ handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
static enum command_return
handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int status;
-
- if (!check_int(client, &status, argv[1], need_integer))
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
return COMMAND_RETURN_ERROR;
- if (status != 0 && status != 1) {
- command_error(client, ACK_ERROR_ARG,
- "\"%i\" is not 0 or 1", status);
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_set_random(&g_playlist, status);
+ playlist_set_random(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
@@ -1279,7 +1189,7 @@ static enum command_return
handle_clearerror(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- pc_clear_error();
+ pc_clear_error(client->player_control);
return COMMAND_RETURN_OK;
}
@@ -1288,7 +1198,6 @@ handle_list(struct client *client, int argc, char *argv[])
{
struct locate_item_list *conditionals;
int tagType = locate_parse_type(argv[1]);
- int ret;
if (tagType < 0) {
command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
@@ -1325,14 +1234,14 @@ handle_list(struct client *client, int argc, char *argv[])
}
}
- ret = listAllUniqueTags(client, tagType, conditionals);
+ GError *error = NULL;
+ enum command_return ret =
+ listAllUniqueTags(client, tagType, conditionals, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(conditionals);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
return ret;
}
@@ -1343,102 +1252,120 @@ handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
int to;
enum playlist_result result;
- if (!check_range(client, &start, &end,
- argv[1], need_range))
+ if (!check_range(client, &start, &end, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2], check_integer, argv[2]))
+ if (!check_int(client, &to, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_range(&g_playlist, start, end, to);
+ result = playlist_move_range(&g_playlist, client->player_control,
+ start, end, to);
return print_playlist_result(client, result);
}
static enum command_return
handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int id, to;
+ unsigned id;
+ int to;
enum playlist_result result;
- if (!check_int(client, &id, argv[1], check_integer, argv[1]))
+ if (!check_unsigned(client, &id, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2], check_integer, argv[2]))
+ if (!check_int(client, &to, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, id, to);
+ result = playlist_move_id(&g_playlist, client->player_control,
+ id, to);
return print_playlist_result(client, result);
}
static enum command_return
handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int song1, song2;
+ unsigned song1, song2;
enum playlist_result result;
- if (!check_int(client, &song1, argv[1], check_integer, argv[1]))
+ if (!check_unsigned(client, &song1, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &song2, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &song2, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs(&g_playlist, song1, song2);
+ result = playlist_swap_songs(&g_playlist, client->player_control,
+ song1, song2);
return print_playlist_result(client, result);
}
static enum command_return
handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int id1, id2;
+ unsigned id1, id2;
enum playlist_result result;
- if (!check_int(client, &id1, argv[1], check_integer, argv[1]))
+ if (!check_unsigned(client, &id1, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &id2, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &id2, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs_id(&g_playlist, id1, id2);
+ result = playlist_swap_songs_id(&g_playlist, client->player_control,
+ id1, id2);
return print_playlist_result(client, result);
}
static enum command_return
handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int song, seek_time;
+ unsigned song, seek_time;
enum playlist_result result;
- if (!check_int(client, &song, argv[1], check_integer, argv[1]))
+ if (!check_unsigned(client, &song, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &seek_time, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_seek_song(&g_playlist, song, seek_time);
+ result = playlist_seek_song(&g_playlist, client->player_control,
+ song, seek_time);
return print_playlist_result(client, result);
}
static enum command_return
handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- int id, seek_time;
+ unsigned id, seek_time;
enum playlist_result result;
- if (!check_int(client, &id, argv[1], check_integer, argv[1]))
+ if (!check_unsigned(client, &id, argv[1]))
return COMMAND_RETURN_ERROR;
- if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_seek_song_id(&g_playlist, client->player_control,
+ id, seek_time);
+ return print_playlist_result(client, result);
+}
+
+static enum command_return
+handle_seekcur(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *p = argv[1];
+ bool relative = *p == '+' || *p == '-';
+ int seek_time;
+ if (!check_int(client, &seek_time, p))
return COMMAND_RETURN_ERROR;
- result = playlist_seek_song_id(&g_playlist, id, seek_time);
+ enum playlist_result result =
+ playlist_seek_current(&g_playlist, client->player_control,
+ seek_time, relative);
return print_playlist_result(client, result);
}
static enum command_return
handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- char *directory = NULL;
- int ret;
+ const char *directory = "";
if (argc == 2)
directory = argv[1];
- ret = printInfoForAllIn(client, directory);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
- return ret;
+ GError *error = NULL;
+ return printInfoForAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1470,7 +1397,7 @@ handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_unsigned(client, &xfade_time, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_cross_fade(xfade_time);
+ pc_set_cross_fade(client->player_control, xfade_time);
return COMMAND_RETURN_OK;
}
@@ -1482,7 +1409,7 @@ handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &db, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_mixramp_db(db);
+ pc_set_mixramp_db(client->player_control, db);
return COMMAND_RETURN_OK;
}
@@ -1494,7 +1421,7 @@ handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &delay_secs, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_mixramp_delay(delay_secs);
+ pc_set_mixramp_delay(client->player_control, delay_secs);
return COMMAND_RETURN_OK;
}
@@ -1556,12 +1483,29 @@ handle_not_commands(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
static enum command_return
-handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+handle_config(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- enum playlist_result result;
+ if (!client_is_local(client)) {
+ command_error(client, ACK_ERROR_PERMISSION,
+ "Command only permitted to local clients");
+ return COMMAND_RETURN_ERROR;
+ }
- result = spl_clear(argv[1]);
- return print_playlist_result(client, result);
+ const char *path = mapper_get_music_directory();
+ if (path != NULL)
+ client_printf(client, "music_directory: %s\n", path);
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_clear(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1569,8 +1513,9 @@ handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *playlist = argv[1];
char *uri = argv[2];
- enum playlist_result result;
+ bool success;
+ GError *error = NULL;
if (uri_has_scheme(uri)) {
if (!uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
@@ -1578,29 +1523,27 @@ handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = spl_append_uri(uri, playlist);
+ success = spl_append_uri(argv[1], playlist, &error);
} else
- result = addAllInToStoredPlaylist(uri, playlist);
+ success = addAllInToStoredPlaylist(uri, playlist, &error);
- if (result == (enum playlist_result)-1) {
+ if (!success && error == NULL) {
command_error(client, ACK_ERROR_NO_EXIST,
"directory or file not found");
return COMMAND_RETURN_ERROR;
}
- return print_playlist_result(client, result);
+ return success ? COMMAND_RETURN_OK : print_error(client, error);
}
static enum command_return
handle_listplaylists(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- GPtrArray *list = spl_list();
- if (list == NULL) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to get list of stored playlists");
- return COMMAND_RETURN_ERROR;
- }
+ GError *error = NULL;
+ GPtrArray *list = spl_list(&error);
+ if (list == NULL)
+ return print_error(client, error);
print_spl_list(client, list);
spl_list_free(list);
@@ -1774,8 +1717,10 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
.name = argv[4],
};
+ db_lock();
directory = db_get_directory(argv[3]);
if (directory == NULL) {
+ db_unlock();
command_error(client, ACK_ERROR_NO_EXIST,
"no such directory");
return COMMAND_RETURN_ERROR;
@@ -1783,6 +1728,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
success = sticker_song_find(directory, data.name,
sticker_song_find_print_cb, &data);
+ db_unlock();
if (!success) {
command_error(client, ACK_ERROR_SYSTEM,
"failed to set search sticker database");
@@ -1817,6 +1763,172 @@ handle_sticker(struct client *client, int argc, char *argv[])
}
#endif
+static enum command_return
+handle_subscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ switch (client_subscribe(client, argv[1])) {
+ case CLIENT_SUBSCRIBE_OK:
+ return COMMAND_RETURN_OK;
+
+ case CLIENT_SUBSCRIBE_INVALID:
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_ALREADY:
+ command_error(client, ACK_ERROR_EXIST,
+ "already subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_FULL:
+ command_error(client, ACK_ERROR_EXIST,
+ "subscription list is full");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* unreachable */
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_unsubscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ if (client_unsubscribe(client, argv[1]))
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "not subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+struct channels_context {
+ GStringChunk *chunk;
+
+ GHashTable *channels;
+};
+
+static void
+collect_channels(gpointer data, gpointer user_data)
+{
+ struct channels_context *context = user_data;
+ const struct client *client = data;
+
+ for (GSList *i = client->subscriptions; i != NULL;
+ i = g_slist_next(i)) {
+ const char *channel = i->data;
+
+ if (g_hash_table_lookup(context->channels, channel) == NULL) {
+ char *channel2 = g_string_chunk_insert(context->chunk,
+ channel);
+ g_hash_table_insert(context->channels, channel2,
+ context);
+ }
+ }
+}
+
+static void
+print_channel(gpointer key, G_GNUC_UNUSED gpointer value, gpointer user_data)
+{
+ struct client *client = user_data;
+ const char *channel = key;
+
+ client_printf(client, "channel: %s\n", channel);
+}
+
+static enum command_return
+handle_channels(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ struct channels_context context = {
+ .chunk = g_string_chunk_new(1024),
+ .channels = g_hash_table_new(g_str_hash, g_str_equal),
+ };
+
+ client_list_foreach(collect_channels, &context);
+
+ g_hash_table_foreach(context.channels, print_channel, client);
+
+ g_hash_table_destroy(context.channels);
+ g_string_chunk_free(context.chunk);
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_read_messages(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ GSList *messages = client_read_messages(client);
+
+ for (GSList *i = messages; i != NULL; i = g_slist_next(i)) {
+ struct client_message *msg = i->data;
+
+ client_printf(client, "channel: %s\nmessage: %s\n",
+ msg->channel, msg->message);
+ client_message_free(msg);
+ }
+
+ g_slist_free(messages);
+
+ return COMMAND_RETURN_OK;
+}
+
+struct send_message_context {
+ struct client_message msg;
+
+ bool sent;
+};
+
+static void
+send_message(gpointer data, gpointer user_data)
+{
+ struct send_message_context *context = user_data;
+ struct client *client = data;
+
+ if (client_push_message(client, &context->msg))
+ context->sent = true;
+}
+
+static enum command_return
+handle_send_message(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 3);
+
+ if (!client_message_valid_channel_name(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ struct send_message_context context = {
+ .sent = false,
+ };
+
+ client_message_init(&context.msg, argv[1], argv[2]);
+
+ client_list_foreach(send_message, &context);
+
+ client_message_deinit(&context.msg);
+
+ if (context.sent)
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "nobody is subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
/**
* The command registry.
*
@@ -1825,10 +1937,12 @@ handle_sticker(struct client *client, int argc, char *argv[])
static const struct command commands[] = {
{ "add", PERMISSION_ADD, 1, 1, handle_add },
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
+ { "channels", PERMISSION_READ, 0, 0, handle_channels },
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
{ "close", PERMISSION_NONE, -1, -1, handle_close },
{ "commands", PERMISSION_NONE, 0, 0, handle_commands },
+ { "config", PERMISSION_ADMIN, 0, 0, handle_config },
{ "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
{ "count", PERMISSION_READ, 2, -1, handle_count },
{ "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
@@ -1848,7 +1962,7 @@ static const struct command commands[] = {
{ "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
{ "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
{ "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
- { "load", PERMISSION_ADD, 1, 1, handle_load },
+ { "load", PERMISSION_ADD, 1, 2, handle_load },
{ "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
{ "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
{ "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
@@ -1874,19 +1988,24 @@ static const struct command commands[] = {
{ "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
{ "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
{ "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
+ { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
+ { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
{ "random", PERMISSION_CONTROL, 1, 1, handle_random },
+ { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
{ "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
{ "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
handle_replay_gain_mode },
{ "replay_gain_status", PERMISSION_READ, 0, 0,
handle_replay_gain_status },
- { "rescan", PERMISSION_ADMIN, 0, 1, handle_rescan },
+ { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
{ "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
{ "save", PERMISSION_CONTROL, 1, 1, handle_save },
{ "search", PERMISSION_READ, 2, -1, handle_search },
{ "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
+ { "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
{ "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
+ { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
{ "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
{ "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
{ "single", PERMISSION_CONTROL, 1, 1, handle_single },
@@ -1896,10 +2015,12 @@ static const struct command commands[] = {
{ "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
#endif
{ "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
+ { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
{ "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
{ "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
{ "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
- { "update", PERMISSION_ADMIN, 0, 1, handle_update },
+ { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
+ { "update", PERMISSION_CONTROL, 0, 1, handle_update },
{ "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
};
@@ -2029,10 +2150,9 @@ static const struct command *
command_checked_lookup(struct client *client, unsigned permission,
int argc, char *argv[])
{
- static char unknown[] = "";
const struct command *cmd;
- current_command = unknown;
+ current_command = "";
if (argc == 0)
return NULL;