diff options
Diffstat (limited to 'src/command/AllCommands.cxx')
-rw-r--r-- | src/command/AllCommands.cxx | 199 |
1 files changed, 102 insertions, 97 deletions
diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 6a4b18198..8e8865ff3 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "AllCommands.hxx" +#include "Request.hxx" #include "QueueCommands.hxx" #include "TagCommands.hxx" #include "PlayerCommands.hxx" @@ -32,11 +33,14 @@ #include "OtherCommands.hxx" #include "Permission.hxx" #include "tag/TagType.h" -#include "protocol/Result.hxx" #include "Partition.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" +#include "util/Macros.hxx" #include "util/Tokenizer.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" +#include "util/StringAPI.hxx" #ifdef ENABLE_SQLITE #include "StickerCommands.hxx" @@ -60,22 +64,22 @@ struct command { unsigned permission; int min; int max; - CommandResult (*handler)(Client &client, unsigned argc, char **argv); + CommandResult (*handler)(Client &client, Request request, Response &response); }; /* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, unsigned argc, char *argv[]); +handle_commands(Client &client, Request request, Response &response); static CommandResult -handle_not_commands(Client &client, unsigned argc, char *argv[]); +handle_not_commands(Client &client, Request request, Response &response); /** * The command registry. * * This array must be sorted! */ -static const struct command commands[] = { +static constexpr struct command commands[] = { { "add", PERMISSION_ADD, 1, 1, handle_add }, { "addid", PERMISSION_ADD, 1, 2, handle_addid }, { "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid }, @@ -194,62 +198,79 @@ static const struct command commands[] = { { "volume", PERMISSION_CONTROL, 1, 1, handle_volume }, }; -static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]); +static constexpr unsigned num_commands = ARRAY_SIZE(commands); static bool command_available(gcc_unused const Partition &partition, gcc_unused const struct command *cmd) { #ifdef ENABLE_SQLITE - if (strcmp(cmd->cmd, "sticker") == 0) + if (StringIsEqual(cmd->cmd, "sticker")) return sticker_enabled(); #endif #ifdef ENABLE_NEIGHBOR_PLUGINS - if (strcmp(cmd->cmd, "listneighbors") == 0) + if (StringIsEqual(cmd->cmd, "listneighbors")) return neighbor_commands_available(partition.instance); #endif + if (StringIsEqual(cmd->cmd, "save") || + StringIsEqual(cmd->cmd, "rm") || + StringIsEqual(cmd->cmd, "rename") || + StringIsEqual(cmd->cmd, "playlistdelete") || + StringIsEqual(cmd->cmd, "playlistmove") || + StringIsEqual(cmd->cmd, "playlistclear") || + StringIsEqual(cmd->cmd, "playlistadd") || + StringIsEqual(cmd->cmd, "listplaylists")) + return playlist_commands_available(); + return true; } -/* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +PrintAvailableCommands(Response &r, const Partition &partition, + unsigned permission) { - const unsigned permission = client.GetPermission(); - const struct command *cmd; - for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission == (permission & cmd->permission) && - command_available(client.partition, cmd)) - client_printf(client, "command: %s\n", cmd->cmd); + command_available(partition, cmd)) + r.Format("command: %s\n", cmd->cmd); } return CommandResult::OK; } static CommandResult -handle_not_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +PrintUnavailableCommands(Response &r, unsigned permission) { - const unsigned permission = client.GetPermission(); - const struct command *cmd; - for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission != (permission & cmd->permission)) - client_printf(client, "command: %s\n", cmd->cmd); + r.Format("command: %s\n", cmd->cmd); } return CommandResult::OK; } -void command_init(void) +/* don't be fooled, this is the command handler for "commands" command */ +static CommandResult +handle_commands(Client &client, gcc_unused Request request, Response &r) +{ + return PrintAvailableCommands(r, client.partition, + client.GetPermission()); +} + +static CommandResult +handle_not_commands(Client &client, gcc_unused Request request, Response &r) +{ + return PrintUnavailableCommands(r, client.GetPermission()); +} + +void +command_init() { #ifndef NDEBUG /* ensure that the command list is sorted */ @@ -258,7 +279,8 @@ void command_init(void) #endif } -void command_finish(void) +void +command_finish() { } @@ -266,13 +288,12 @@ static const struct command * command_lookup(const char *name) { unsigned a = 0, b = num_commands, i; - int cmp; /* binary search */ do { i = (a + b) / 2; - cmp = strcmp(name, commands[i].cmd); + const auto cmp = strcmp(name, commands[i].cmd); if (cmp == 0) return &commands[i]; else if (cmp < 0) @@ -285,60 +306,53 @@ command_lookup(const char *name) } static bool -command_check_request(const struct command *cmd, Client &client, - unsigned permission, unsigned argc, char *argv[]) +command_check_request(const struct command *cmd, Response &r, + unsigned permission, Request args) { - const unsigned min = cmd->min + 1; - const unsigned max = cmd->max + 1; - if (cmd->permission != (permission & cmd->permission)) { - command_error(client, ACK_ERROR_PERMISSION, + r.FormatError(ACK_ERROR_PERMISSION, "you don't have permission for \"%s\"", cmd->cmd); return false; } - if (min == 0) + const int min = cmd->min; + const int max = cmd->max; + + if (min < 0) return true; - if (min == max && max != argc) { - command_error(client, ACK_ERROR_ARG, + if (min == max && unsigned(max) != args.size) { + r.FormatError(ACK_ERROR_ARG, "wrong number of arguments for \"%s\"", - argv[0]); + cmd->cmd); return false; - } else if (argc < min) { - command_error(client, ACK_ERROR_ARG, - "too few arguments for \"%s\"", argv[0]); + } else if (args.size < unsigned(min)) { + r.FormatError(ACK_ERROR_ARG, + "too few arguments for \"%s\"", cmd->cmd); return false; - } else if (argc > max && max /* != 0 */ ) { - command_error(client, ACK_ERROR_ARG, - "too many arguments for \"%s\"", argv[0]); + } else if (max >= 0 && args.size > unsigned(max)) { + r.FormatError(ACK_ERROR_ARG, + "too many arguments for \"%s\"", cmd->cmd); return false; } else return true; } static const struct command * -command_checked_lookup(Client &client, unsigned permission, - unsigned argc, char *argv[]) +command_checked_lookup(Response &r, unsigned permission, + const char *cmd_name, Request args) { - const struct command *cmd; - - current_command = ""; - - if (argc == 0) - return nullptr; - - cmd = command_lookup(argv[0]); + const struct command *cmd = command_lookup(cmd_name); if (cmd == nullptr) { - command_error(client, ACK_ERROR_UNKNOWN, - "unknown command \"%s\"", argv[0]); + r.FormatError(ACK_ERROR_UNKNOWN, + "unknown command \"%s\"", cmd_name); return nullptr; } - current_command = cmd->cmd; + r.SetCommand(cmd->cmd); - if (!command_check_request(cmd, client, permission, argc, argv)) + if (!command_check_request(cmd, r, permission, args)) return nullptr; return cmd; @@ -347,68 +361,59 @@ command_checked_lookup(Client &client, unsigned permission, CommandResult command_process(Client &client, unsigned num, char *line) { + Response r(client, num); Error error; - char *argv[COMMAND_ARGV_MAX] = { nullptr }; - const struct command *cmd; - CommandResult ret = CommandResult::ERROR; - - command_list_num = num; /* get the command name (first word on the line) */ + /* we have to set current_command because Response::Error() + expects it to be set */ Tokenizer tokenizer(line); - argv[0] = tokenizer.NextWord(error); - if (argv[0] == nullptr) { - current_command = ""; + + const char *const cmd_name = tokenizer.NextWord(error); + if (cmd_name == nullptr) { if (tokenizer.IsEnd()) - command_error(client, ACK_ERROR_UNKNOWN, - "No command given"); + r.FormatError(ACK_ERROR_UNKNOWN, "No command given"); else - command_error(client, ACK_ERROR_UNKNOWN, - "%s", error.GetMessage()); - - current_command = nullptr; + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); /* this client does not speak the MPD protocol; kick the connection */ return CommandResult::FINISH; } - unsigned argc = 1; + char *argv[COMMAND_ARGV_MAX]; + Request args(argv, 0); /* now parse the arguments (quoted or unquoted) */ - while (argc < COMMAND_ARGV_MAX && - (argv[argc] = - tokenizer.NextParam(error)) != nullptr) - ++argc; - - /* some error checks; we have to set current_command because - command_error() expects it to be set */ + while (true) { + if (args.size == COMMAND_ARGV_MAX) { + r.Error(ACK_ERROR_ARG, "Too many arguments"); + return CommandResult::ERROR; + } - current_command = argv[0]; + char *a = tokenizer.NextParam(error); + if (a == nullptr) { + if (tokenizer.IsEnd()) + break; - if (argc >= COMMAND_ARGV_MAX) { - command_error(client, ACK_ERROR_ARG, "Too many arguments"); - current_command = nullptr; - return CommandResult::ERROR; - } + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); + return CommandResult::ERROR; + } - if (!tokenizer.IsEnd()) { - command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage()); - current_command = nullptr; - return CommandResult::ERROR; + argv[args.size++] = a; } /* look up and invoke the command handler */ - cmd = command_checked_lookup(client, client.GetPermission(), - argc, argv); - if (cmd) - ret = cmd->handler(client, argc, argv); + const struct command *cmd = + command_checked_lookup(r, client.GetPermission(), + cmd_name, args); - current_command = nullptr; - command_list_num = 0; + CommandResult ret = cmd + ? cmd->handler(client, args, r) + : CommandResult::ERROR; return ret; } |