diff options
Diffstat (limited to 'src/command.c')
-rw-r--r-- | src/command.c | 336 |
1 files changed, 296 insertions, 40 deletions
diff --git a/src/command.c b/src/command.c index 64f161805..3e60e0ac3 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 @@ -44,6 +44,9 @@ #include "dbUtils.h" #include "tag.h" #include "client.h" +#include "client_idle.h" +#include "client_internal.h" +#include "client_subscribe.h" #include "tag_print.h" #include "path.h" #include "replay_gain_config.h" @@ -434,7 +437,7 @@ handle_play(struct client *client, int argc, char *argv[]) if (argc == 2 && !check_int(client, &song, argv[1], need_positive)) 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); } @@ -447,7 +450,7 @@ handle_playid(struct client *client, int argc, char *argv[]) if (argc == 2 && !check_int(client, &id, argv[1], need_positive)) 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 +458,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 +479,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 +496,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 +529,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 +564,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", @@ -605,6 +608,7 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) result = PLAYLIST_RESULT_DENIED; #else result = playlist_append_file(&g_playlist, + client->player_control, uri + 7, client_get_uid(client), NULL); #endif @@ -618,11 +622,13 @@ 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); + result = addAllIn(client->player_control, uri); if (result == (enum playlist_result)-1) { command_error(client, ACK_ERROR_NO_EXIST, "directory or file not found"); @@ -643,7 +649,9 @@ handle_addid(struct client *client, int argc, char *argv[]) #ifdef WIN32 result = PLAYLIST_RESULT_DENIED; #else - result = playlist_append_file(&g_playlist, uri + 7, + result = playlist_append_file(&g_playlist, + client->player_control, + uri + 7, client_get_uid(client), &added_id); #endif @@ -654,7 +662,9 @@ 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) @@ -664,11 +674,13 @@ handle_addid(struct client *client, int argc, char *argv[]) int to; if (!check_int(client, &to, argv[2], check_integer, 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; } } @@ -686,7 +698,8 @@ handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) if (!check_range(client, &start, &end, argv[1], need_range)) 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); } @@ -699,7 +712,7 @@ handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) if (!check_int(client, &id, argv[1], need_positive)) 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); } @@ -720,7 +733,7 @@ handle_shuffle(G_GNUC_UNUSED struct client *client, argv[1], need_range)) 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 +741,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; } @@ -747,11 +760,13 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) { enum playlist_result result; - result = playlist_open_into_queue(argv[1], &g_playlist); + result = playlist_open_into_queue(argv[1], &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]); + result = playlist_load_spl(&g_playlist, client->player_control, + argv[1]); return print_playlist_result(client, result); } @@ -1141,7 +1156,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,7 +1166,69 @@ 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], need_range)) + 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; } @@ -1210,7 +1287,7 @@ handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; } - playlist_set_repeat(&g_playlist, status); + playlist_set_repeat(&g_playlist, client->player_control, status); return COMMAND_RETURN_OK; } @@ -1228,7 +1305,7 @@ handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; } - playlist_set_single(&g_playlist, status); + playlist_set_single(&g_playlist, client->player_control, status); return COMMAND_RETURN_OK; } @@ -1264,7 +1341,7 @@ handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) 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 +1356,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; } @@ -1348,7 +1425,8 @@ handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; if (!check_int(client, &to, argv[2], check_integer, 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); } @@ -1362,7 +1440,8 @@ handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; if (!check_int(client, &to, argv[2], check_integer, 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); } @@ -1376,7 +1455,8 @@ handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; if (!check_int(client, &song2, argv[2], check_integer, 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); } @@ -1390,7 +1470,8 @@ handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) return COMMAND_RETURN_ERROR; if (!check_int(client, &id2, argv[2], check_integer, 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); } @@ -1405,7 +1486,8 @@ handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) if (!check_int(client, &seek_time, argv[2], check_integer, 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); } @@ -1420,7 +1502,8 @@ handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[]) if (!check_int(client, &seek_time, argv[2], check_integer, argv[2])) return COMMAND_RETURN_ERROR; - result = playlist_seek_song_id(&g_playlist, id, seek_time); + result = playlist_seek_song_id(&g_playlist, client->player_control, + id, seek_time); return print_playlist_result(client, result); } @@ -1470,7 +1553,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 +1565,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 +1577,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; } @@ -1817,6 +1900,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,6 +2074,7 @@ 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 }, @@ -1874,19 +2124,23 @@ 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 }, { "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 +2150,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 }, }; |