aboutsummaryrefslogtreecommitdiffstats
path: root/src/command/OtherCommands.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/command/OtherCommands.cxx')
-rw-r--r--src/command/OtherCommands.cxx336
1 files changed, 172 insertions, 164 deletions
diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx
index a924f77b5..b4a23fe4b 100644
--- a/src/command/OtherCommands.cxx
+++ b/src/command/OtherCommands.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,11 +19,13 @@
#include "config.h"
#include "OtherCommands.hxx"
+#include "Request.hxx"
#include "FileCommands.hxx"
#include "StorageCommands.hxx"
#include "CommandError.hxx"
#include "db/Uri.hxx"
#include "storage/StorageInterface.hxx"
+#include "LocateUri.hxx"
#include "DetachedSong.hxx"
#include "SongPrint.hxx"
#include "TagPrint.hxx"
@@ -31,18 +33,19 @@
#include "tag/TagHandler.hxx"
#include "TimePrint.hxx"
#include "decoder/DecoderPrint.hxx"
-#include "protocol/ArgParser.hxx"
-#include "protocol/Result.hxx"
#include "ls.hxx"
#include "mixer/Volume.hxx"
#include "util/UriUtil.hxx"
#include "util/Error.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/StringAPI.hxx"
#include "fs/AllocatedPath.hxx"
#include "Stats.hxx"
#include "Permission.hxx"
#include "PlaylistFile.hxx"
#include "db/PlaylistVector.hxx"
#include "client/Client.hxx"
+#include "client/Response.hxx"
#include "Partition.hxx"
#include "Instance.hxx"
#include "Idle.hxx"
@@ -57,52 +60,51 @@
#include <string.h>
static void
-print_spl_list(Client &client, const PlaylistVector &list)
+print_spl_list(Response &r, const PlaylistVector &list)
{
for (const auto &i : list) {
- client_printf(client, "playlist: %s\n", i.name.c_str());
+ r.Format("playlist: %s\n", i.name.c_str());
if (i.mtime > 0)
- time_print(client, "Last-Modified", i.mtime);
+ time_print(r, "Last-Modified", i.mtime);
}
}
CommandResult
-handle_urlhandlers(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_urlhandlers(Client &client, gcc_unused Request args, Response &r)
{
if (client.IsLocal())
- client_puts(client, "handler: file://\n");
- print_supported_uri_schemes(client);
+ r.Format("handler: file://\n");
+ print_supported_uri_schemes(r);
return CommandResult::OK;
}
CommandResult
-handle_decoders(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_decoders(gcc_unused Client &client, gcc_unused Request args,
+ Response &r)
{
- decoder_list_print(client);
+ decoder_list_print(r);
return CommandResult::OK;
}
CommandResult
-handle_tagtypes(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_tagtypes(gcc_unused Client &client, gcc_unused Request request,
+ Response &r)
{
- tag_print_types(client);
+ tag_print_types(r);
return CommandResult::OK;
}
CommandResult
-handle_kill(gcc_unused Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_kill(gcc_unused Client &client, gcc_unused Request request,
+ gcc_unused Response &r)
{
return CommandResult::KILL;
}
CommandResult
-handle_close(gcc_unused Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_close(gcc_unused Client &client, gcc_unused Request args,
+ gcc_unused Response &r)
{
return CommandResult::FINISH;
}
@@ -110,44 +112,60 @@ handle_close(gcc_unused Client &client,
static void
print_tag(TagType type, const char *value, void *ctx)
{
- Client &client = *(Client *)ctx;
+ auto &r = *(Response *)ctx;
- tag_print(client, type, value);
+ tag_print(r, type, value);
}
CommandResult
-handle_listfiles(Client &client, unsigned argc, char *argv[])
+handle_listfiles(Client &client, Request args, Response &r)
{
- const char *const uri = argc == 2
- ? argv[1]
- /* default is root directory */
- : "";
-
- if (memcmp(uri, "file:///", 8) == 0)
- /* list local directory */
- return handle_listfiles_local(client, uri + 7);
+ /* default is root directory */
+ const auto uri = args.GetOptional(0, "");
+ Error error;
+ const auto located_uri = LocateUri(uri, &client,
#ifdef ENABLE_DATABASE
- if (uri_has_scheme(uri))
- /* use storage plugin to list remote directory */
- return handle_listfiles_storage(client, uri);
+ nullptr,
+#endif
+ error);
- /* must be a path relative to the configured
- music_directory */
+ switch (located_uri.type) {
+ case LocatedUri::Type::UNKNOWN:
+ return print_error(r, error);
- if (client.partition.instance.storage != nullptr)
- /* if we have a storage instance, obtain a list of
- files from it */
- return handle_listfiles_storage(client,
- *client.partition.instance.storage,
- uri);
+ case LocatedUri::Type::ABSOLUTE:
+#ifdef ENABLE_DATABASE
+ /* use storage plugin to list remote directory */
+ return handle_listfiles_storage(r, located_uri.canonical_uri);
+#else
+ r.Error(ACK_ERROR_NO_EXIST, "No database");
+ return CommandResult::ERROR;
+#endif
- /* fall back to entries from database if we have no storage */
- return handle_listfiles_db(client, uri);
+ case LocatedUri::Type::RELATIVE:
+#ifdef ENABLE_DATABASE
+ if (client.partition.instance.storage != nullptr)
+ /* if we have a storage instance, obtain a list of
+ files from it */
+ return handle_listfiles_storage(r,
+ *client.partition.instance.storage,
+ uri);
+
+ /* fall back to entries from database if we have no storage */
+ return handle_listfiles_db(client, r, uri);
#else
- command_error(client, ACK_ERROR_NO_EXIST, "No database");
- return CommandResult::ERROR;
+ r.Error(ACK_ERROR_NO_EXIST, "No database");
+ return CommandResult::ERROR;
#endif
+
+ case LocatedUri::Type::PATH:
+ /* list local directory */
+ return handle_listfiles_local(r, located_uri.canonical_uri,
+ located_uri.path);
+ }
+
+ gcc_unreachable();
}
static constexpr tag_handler print_tag_handler = {
@@ -156,69 +174,35 @@ static constexpr tag_handler print_tag_handler = {
nullptr,
};
-CommandResult
-handle_lsinfo(Client &client, unsigned argc, char *argv[])
+static CommandResult
+handle_lsinfo_absolute(Response &r, const char *uri)
{
- const char *const uri = argc == 2
- ? argv[1]
- /* default is root directory */
- : "";
-
- if (memcmp(uri, "file:///", 8) == 0) {
- /* print information about an arbitrary local file */
- const char *path_utf8 = uri + 7;
- const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
-
- if (path_fs.IsNull()) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported file name");
- return CommandResult::ERROR;
- }
-
- Error error;
- if (!client.AllowFile(path_fs, error))
- return print_error(client, error);
-
- DetachedSong song(path_utf8);
- if (!song.Update()) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such file");
- return CommandResult::ERROR;
- }
-
- song_print_info(client, song);
- return CommandResult::OK;
+ if (!tag_stream_scan(uri, print_tag_handler, &r)) {
+ r.Error(ACK_ERROR_NO_EXIST, "No such file");
+ return CommandResult::ERROR;
}
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return CommandResult::ERROR;
- }
-
- if (!tag_stream_scan(uri, print_tag_handler, &client)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such file");
- return CommandResult::ERROR;
- }
-
- return CommandResult::OK;
- }
+ return CommandResult::OK;
+}
+static CommandResult
+handle_lsinfo_relative(Client &client, Response &r, const char *uri)
+{
#ifdef ENABLE_DATABASE
- CommandResult result = handle_lsinfo2(client, argc, argv);
+ CommandResult result = handle_lsinfo2(client, uri, r);
if (result != CommandResult::OK)
return result;
+#else
+ (void)client;
#endif
if (isRootDirectory(uri)) {
Error error;
const auto &list = ListPlaylistFiles(error);
- print_spl_list(client, list);
+ print_spl_list(r, list);
} else {
#ifndef ENABLE_DATABASE
- command_error(client, ACK_ERROR_NO_EXIST, "No database");
+ r.Error(ACK_ERROR_NO_EXIST, "No database");
return CommandResult::ERROR;
#endif
}
@@ -226,38 +210,84 @@ handle_lsinfo(Client &client, unsigned argc, char *argv[])
return CommandResult::OK;
}
+static CommandResult
+handle_lsinfo_path(Client &client, Response &r,
+ const char *path_utf8, Path path_fs)
+{
+ DetachedSong song(path_utf8);
+ if (!song.LoadFile(path_fs)) {
+ r.Error(ACK_ERROR_NO_EXIST, "No such file");
+ return CommandResult::ERROR;
+ }
+
+ song_print_info(r, client.partition, song);
+ return CommandResult::OK;
+}
+
+CommandResult
+handle_lsinfo(Client &client, Request args, Response &r)
+{
+ /* default is root directory */
+ const auto uri = args.GetOptional(0, "");
+
+ Error error;
+ const auto located_uri = LocateUri(uri, &client,
+#ifdef ENABLE_DATABASE
+ nullptr,
+#endif
+ error);
+
+ switch (located_uri.type) {
+ case LocatedUri::Type::UNKNOWN:
+ return print_error(r, error);
+
+ case LocatedUri::Type::ABSOLUTE:
+ return handle_lsinfo_absolute(r, located_uri.canonical_uri);
+
+ case LocatedUri::Type::RELATIVE:
+ return handle_lsinfo_relative(client, r,
+ located_uri.canonical_uri);
+
+ case LocatedUri::Type::PATH:
+ /* print information about an arbitrary local file */
+ return handle_lsinfo_path(client, r, located_uri.canonical_uri,
+ located_uri.path);
+ }
+
+ gcc_unreachable();
+}
+
#ifdef ENABLE_DATABASE
static CommandResult
-handle_update(Client &client, UpdateService &update,
+handle_update(Response &r, UpdateService &update,
const char *uri_utf8, bool discard)
{
unsigned ret = update.Enqueue(uri_utf8, discard);
if (ret > 0) {
- client_printf(client, "updating_db: %i\n", ret);
+ r.Format("updating_db: %i\n", ret);
return CommandResult::OK;
} else {
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
+ r.Error(ACK_ERROR_UPDATE_ALREADY, "already updating");
return CommandResult::ERROR;
}
}
static CommandResult
-handle_update(Client &client, Database &db,
+handle_update(Response &r, Database &db,
const char *uri_utf8, bool discard)
{
Error error;
unsigned id = db.Update(uri_utf8, discard, error);
if (id > 0) {
- client_printf(client, "updating_db: %i\n", id);
+ r.Format("updating_db: %i\n", id);
return CommandResult::OK;
} else if (error.IsDefined()) {
- return print_error(client, error);
+ return print_error(r, error);
} else {
/* Database::Update() has returned 0 without setting
the Error: the method is not implemented */
- command_error(client, ACK_ERROR_NO_EXIST, "Not implemented");
+ r.Error(ACK_ERROR_NO_EXIST, "Not implemented");
return CommandResult::ERROR;
}
}
@@ -265,72 +295,62 @@ handle_update(Client &client, Database &db,
#endif
static CommandResult
-handle_update(Client &client, unsigned argc, char *argv[], bool discard)
+handle_update(Client &client, Request args, Response &r, bool discard)
{
#ifdef ENABLE_DATABASE
const char *path = "";
- assert(argc <= 2);
- if (argc == 2) {
- path = argv[1];
+ assert(args.size <= 1);
+ if (!args.IsEmpty()) {
+ path = args.front();
- if (*path == 0 || strcmp(path, "/") == 0)
+ if (*path == 0 || StringIsEqual(path, "/"))
/* backwards compatibility with MPD 0.15 */
path = "";
else if (!uri_safe_local(path)) {
- command_error(client, ACK_ERROR_ARG,
- "Malformed path");
+ r.Error(ACK_ERROR_ARG, "Malformed path");
return CommandResult::ERROR;
}
}
UpdateService *update = client.partition.instance.update;
if (update != nullptr)
- return handle_update(client, *update, path, discard);
+ return handle_update(r, *update, path, discard);
Database *db = client.partition.instance.database;
if (db != nullptr)
- return handle_update(client, *db, path, discard);
+ return handle_update(r, *db, path, discard);
#else
- (void)argc;
- (void)argv;
+ (void)client;
+ (void)args;
(void)discard;
#endif
- command_error(client, ACK_ERROR_NO_EXIST, "No database");
+ r.Error(ACK_ERROR_NO_EXIST, "No database");
return CommandResult::ERROR;
}
CommandResult
-handle_update(Client &client, gcc_unused unsigned argc, char *argv[])
+handle_update(Client &client, Request args, gcc_unused Response &r)
{
- return handle_update(client, argc, argv, false);
+ return handle_update(client, args, r, false);
}
CommandResult
-handle_rescan(Client &client, gcc_unused unsigned argc, char *argv[])
+handle_rescan(Client &client, Request args, Response &r)
{
- return handle_update(client, argc, argv, true);
+ return handle_update(client, args, r, true);
}
CommandResult
-handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[])
+handle_setvol(Client &client, Request args, Response &r)
{
unsigned level;
- bool success;
-
- if (!check_unsigned(client, &level, argv[1]))
+ if (!args.Parse(0, level, r, 100))
return CommandResult::ERROR;
- if (level > 100) {
- command_error(client, ACK_ERROR_ARG, "Invalid volume value");
- return CommandResult::ERROR;
- }
-
- success = volume_level_change(client.partition.outputs, level);
- if (!success) {
- command_error(client, ACK_ERROR_SYSTEM,
- "problems setting volume");
+ if (!volume_level_change(client.partition.outputs, level)) {
+ r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
return CommandResult::ERROR;
}
@@ -338,20 +358,15 @@ handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[])
}
CommandResult
-handle_volume(Client &client, gcc_unused unsigned argc, char *argv[])
+handle_volume(Client &client, Request args, Response &r)
{
int relative;
- if (!check_int(client, &relative, argv[1]))
+ if (!args.Parse(0, relative, r, -100, 100))
return CommandResult::ERROR;
- if (relative < -100 || relative > 100) {
- command_error(client, ACK_ERROR_ARG, "Invalid volume value");
- return CommandResult::ERROR;
- }
-
const int old_volume = volume_level_get(client.partition.outputs);
if (old_volume < 0) {
- command_error(client, ACK_ERROR_SYSTEM, "No mixer");
+ r.Error(ACK_ERROR_SYSTEM, "No mixer");
return CommandResult::ERROR;
}
@@ -363,8 +378,7 @@ handle_volume(Client &client, gcc_unused unsigned argc, char *argv[])
if (new_volume != old_volume &&
!volume_level_change(client.partition.outputs, new_volume)) {
- command_error(client, ACK_ERROR_SYSTEM,
- "problems setting volume");
+ r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
return CommandResult::ERROR;
}
@@ -372,27 +386,25 @@ handle_volume(Client &client, gcc_unused unsigned argc, char *argv[])
}
CommandResult
-handle_stats(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_stats(Client &client, gcc_unused Request args, Response &r)
{
- stats_print(client);
+ stats_print(r, client.partition);
return CommandResult::OK;
}
CommandResult
-handle_ping(gcc_unused Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_ping(gcc_unused Client &client, gcc_unused Request args,
+ gcc_unused Response &r)
{
return CommandResult::OK;
}
CommandResult
-handle_password(Client &client, gcc_unused unsigned argc, char *argv[])
+handle_password(Client &client, Request args, Response &r)
{
unsigned permission = 0;
-
- if (getPermissionFromPassword(argv[1], &permission) < 0) {
- command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
+ if (getPermissionFromPassword(args.front(), &permission) < 0) {
+ r.Error(ACK_ERROR_PASSWORD, "incorrect password");
return CommandResult::ERROR;
}
@@ -402,12 +414,11 @@ handle_password(Client &client, gcc_unused unsigned argc, char *argv[])
}
CommandResult
-handle_config(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_config(Client &client, gcc_unused Request args, Response &r)
{
if (!client.IsLocal()) {
- command_error(client, ACK_ERROR_PERMISSION,
- "Command only permitted to local clients");
+ r.Error(ACK_ERROR_PERMISSION,
+ "Command only permitted to local clients");
return CommandResult::ERROR;
}
@@ -415,7 +426,7 @@ handle_config(Client &client,
const Storage *storage = client.GetStorage();
if (storage != nullptr) {
const auto path = storage->MapUTF8("");
- client_printf(client, "music_directory: %s\n", path.c_str());
+ r.Format("music_directory: %s\n", path.c_str());
}
#endif
@@ -423,17 +434,14 @@ handle_config(Client &client,
}
CommandResult
-handle_idle(Client &client,
- gcc_unused unsigned argc, gcc_unused char *argv[])
+handle_idle(Client &client, Request args, Response &r)
{
unsigned flags = 0;
-
- for (unsigned i = 1; i < argc; ++i) {
- unsigned event = idle_parse_name(argv[i]);
+ for (const char *i : args) {
+ unsigned event = idle_parse_name(i);
if (event == 0) {
- command_error(client, ACK_ERROR_ARG,
- "Unrecognized idle event: %s",
- argv[i]);
+ r.FormatError(ACK_ERROR_ARG,
+ "Unrecognized idle event: %s", i);
return CommandResult::ERROR;
}