diff --git a/Makefile.am b/Makefile.am index 1889d26dd..6e56b0a6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -122,6 +122,7 @@ libmpd_a_SOURCES = \ src/client/ClientMessage.cxx src/client/ClientMessage.hxx \ src/client/ClientSubscribe.cxx \ src/client/ClientFile.cxx \ + src/client/Response.cxx src/client/Response.hxx \ src/Listen.cxx src/Listen.hxx \ src/LogInit.cxx src/LogInit.hxx \ src/LogBackend.cxx src/LogBackend.hxx \ diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index 069fbf430..ee3794bb9 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -27,6 +27,7 @@ #include "Instance.hxx" #include "db/Interface.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "input/InputStream.hxx" #include "DetachedSong.hxx" #include "fs/Traits.hxx" @@ -37,15 +38,17 @@ #define SONG_TIME "Time: " void -playlist_print_uris(Client &client, const playlist &playlist) +playlist_print_uris(Response &r, Partition &partition, + const playlist &playlist) { const Queue &queue = playlist.queue; - queue_print_uris(client, queue, 0, queue.GetLength()); + queue_print_uris(r, partition, queue, 0, queue.GetLength()); } bool -playlist_print_info(Client &client, const playlist &playlist, +playlist_print_info(Response &r, Partition &partition, + const playlist &playlist, unsigned start, unsigned end) { const Queue &queue = playlist.queue; @@ -58,12 +61,12 @@ playlist_print_info(Client &client, const playlist &playlist, /* an invalid "start" offset is fatal */ return false; - queue_print_info(client, queue, start, end); + queue_print_info(r, partition, queue, start, end); return true; } bool -playlist_print_id(Client &client, const playlist &playlist, +playlist_print_id(Response &r, Partition &partition, const playlist &playlist, unsigned id) { int position; @@ -73,50 +76,53 @@ playlist_print_id(Client &client, const playlist &playlist, /* no such song */ return false; - return playlist_print_info(client, playlist, position, position + 1); + return playlist_print_info(r, partition, + playlist, position, position + 1); } bool -playlist_print_current(Client &client, const playlist &playlist) +playlist_print_current(Response &r, Partition &partition, + const playlist &playlist) { int current_position = playlist.GetCurrentPosition(); if (current_position < 0) return false; - queue_print_info(client, playlist.queue, + queue_print_info(r, partition, playlist.queue, current_position, current_position + 1); return true; } void -playlist_print_find(Client &client, const playlist &playlist, +playlist_print_find(Response &r, Partition &partition, + const playlist &playlist, const SongFilter &filter) { - queue_find(client, playlist.queue, filter); + queue_find(r, partition, playlist.queue, filter); } void -playlist_print_changes_info(Client &client, +playlist_print_changes_info(Response &r, Partition &partition, const playlist &playlist, uint32_t version) { - queue_print_changes_info(client, playlist.queue, version); + queue_print_changes_info(r, partition, playlist.queue, version); } void -playlist_print_changes_position(Client &client, +playlist_print_changes_position(Response &r, const playlist &playlist, uint32_t version) { - queue_print_changes_position(client, playlist.queue, version); + queue_print_changes_position(r, playlist.queue, version); } #ifdef ENABLE_DATABASE static bool -PrintSongDetails(Client &client, const char *uri_utf8) +PrintSongDetails(Response &r, Partition &partition, const char *uri_utf8) { - const Database *db = client.partition.instance.database; + const Database *db = partition.instance.database; if (db == nullptr) return false; @@ -124,7 +130,7 @@ PrintSongDetails(Client &client, const char *uri_utf8) if (song == nullptr) return false; - song_print_info(client, *song); + song_print_info(r, partition, *song); db->ReturnSong(song); return true; } @@ -132,7 +138,8 @@ PrintSongDetails(Client &client, const char *uri_utf8) #endif bool -spl_print(Client &client, const char *name_utf8, bool detail, +spl_print(Response &r, Partition &partition, + const char *name_utf8, bool detail, Error &error) { #ifndef ENABLE_DATABASE @@ -145,10 +152,10 @@ spl_print(Client &client, const char *name_utf8, bool detail, for (const auto &uri_utf8 : contents) { #ifdef ENABLE_DATABASE - if (!detail || !PrintSongDetails(client, uri_utf8.c_str())) + if (!detail || !PrintSongDetails(r, partition, + uri_utf8.c_str())) #endif - client_printf(client, SONG_FILE "%s\n", - uri_utf8.c_str()); + r.Format(SONG_FILE "%s\n", uri_utf8.c_str()); } return true; diff --git a/src/PlaylistPrint.hxx b/src/PlaylistPrint.hxx index e0fcc2c2d..bc4c2cb47 100644 --- a/src/PlaylistPrint.hxx +++ b/src/PlaylistPrint.hxx @@ -23,15 +23,18 @@ #include struct playlist; +struct Partition; class SongFilter; class Client; +class Response; class Error; /** * Sends the whole playlist to the client, song URIs only. */ void -playlist_print_uris(Client &client, const playlist &playlist); +playlist_print_uris(Response &r, Partition &partition, + const playlist &playlist); /** * Sends a range of the playlist to the client, including all known @@ -40,7 +43,8 @@ playlist_print_uris(Client &client, const playlist &playlist); * This function however fails when the start offset is invalid. */ bool -playlist_print_info(Client &client, const playlist &playlist, +playlist_print_info(Response &r, Partition &partition, + const playlist &playlist, unsigned start, unsigned end); /** @@ -49,8 +53,8 @@ playlist_print_info(Client &client, const playlist &playlist, * @return true on suite, false if there is no such song */ bool -playlist_print_id(Client &client, const playlist &playlist, - unsigned id); +playlist_print_id(Response &r, Partition &partition, + const playlist &playlist, unsigned id); /** * Sends the current song to the client. @@ -58,20 +62,22 @@ playlist_print_id(Client &client, const playlist &playlist, * @return true on success, false if there is no current song */ bool -playlist_print_current(Client &client, const playlist &playlist); +playlist_print_current(Response &r, Partition &partition, + const playlist &playlist); /** * Find songs in the playlist. */ void -playlist_print_find(Client &client, const playlist &playlist, +playlist_print_find(Response &r, Partition &partition, + const playlist &playlist, const SongFilter &filter); /** * Print detailed changes since the specified playlist version. */ void -playlist_print_changes_info(Client &client, +playlist_print_changes_info(Response &r, Partition &partition, const playlist &playlist, uint32_t version); @@ -79,7 +85,7 @@ playlist_print_changes_info(Client &client, * Print changes since the specified playlist version, position only. */ void -playlist_print_changes_position(Client &client, +playlist_print_changes_position(Response &r, const playlist &playlist, uint32_t version); @@ -92,7 +98,8 @@ playlist_print_changes_position(Client &client, * @return true on success, false if the playlist does not exist */ bool -spl_print(Client &client, const char *name_utf8, bool detail, +spl_print(Response &r, Partition &partition, + const char *name_utf8, bool detail, Error &error); #endif diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index 9d10d690a..804920f9b 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -20,18 +20,20 @@ #include "config.h" #include "SongPrint.hxx" #include "db/LightSong.hxx" +#include "Partition.hxx" +#include "Instance.hxx" #include "storage/StorageInterface.hxx" #include "DetachedSong.hxx" #include "TimePrint.hxx" #include "TagPrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" #include "fs/Traits.hxx" #include "util/UriUtil.hxx" #define SONG_FILE "file: " static void -song_print_uri(Client &client, const char *uri, bool base) +song_print_uri(Response &r, Partition &partition, const char *uri, bool base) { std::string allocated; @@ -39,12 +41,14 @@ song_print_uri(Client &client, const char *uri, bool base) uri = PathTraitsUTF8::GetBase(uri); } else { #ifdef ENABLE_DATABASE - const Storage *storage = client.GetStorage(); + const Storage *storage = partition.instance.storage; if (storage != nullptr) { const char *suffix = storage->MapToRelativeUTF8(uri); if (suffix != nullptr) uri = suffix; } +#else + (void)partition; #endif allocated = uri_remove_auth(uri); @@ -52,78 +56,81 @@ song_print_uri(Client &client, const char *uri, bool base) uri = allocated.c_str(); } - client_printf(client, SONG_FILE "%s\n", uri); + r.Format(SONG_FILE "%s\n", uri); } void -song_print_uri(Client &client, const LightSong &song, bool base) +song_print_uri(Response &r, Partition &partition, + const LightSong &song, bool base) { - if (!base && song.directory != nullptr) { - client_printf(client, SONG_FILE "%s/%s\n", - song.directory, song.uri); - } else - song_print_uri(client, song.uri, base); + if (!base && song.directory != nullptr) + r.Format(SONG_FILE "%s/%s\n", song.directory, song.uri); + else + song_print_uri(r, partition, song.uri, base); } void -song_print_uri(Client &client, const DetachedSong &song, bool base) +song_print_uri(Response &r, Partition &partition, + const DetachedSong &song, bool base) { - song_print_uri(client, song.GetURI(), base); + song_print_uri(r, partition, song.GetURI(), base); } void -song_print_info(Client &client, const LightSong &song, bool base) +song_print_info(Response &r, Partition &partition, + const LightSong &song, bool base) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); const unsigned start_ms = song.start_time.ToMS(); const unsigned end_ms = song.end_time.ToMS(); if (end_ms > 0) - client_printf(client, "Range: %u.%03u-%u.%03u\n", - start_ms / 1000, - start_ms % 1000, - end_ms / 1000, - end_ms % 1000); + r.Format("Range: %u.%03u-%u.%03u\n", + start_ms / 1000, + start_ms % 1000, + end_ms / 1000, + end_ms % 1000); else if (start_ms > 0) - client_printf(client, "Range: %u.%03u-\n", - start_ms / 1000, - start_ms % 1000); + r.Format("Range: %u.%03u-\n", + start_ms / 1000, + start_ms % 1000); if (song.mtime > 0) - time_print(client, "Last-Modified", song.mtime); + time_print(r, "Last-Modified", song.mtime); - tag_print(client, *song.tag); + tag_print(r, *song.tag); } void -song_print_info(Client &client, const DetachedSong &song, bool base) +song_print_info(Response &r, Partition &partition, + const DetachedSong &song, bool base) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); const unsigned start_ms = song.GetStartTime().ToMS(); const unsigned end_ms = song.GetEndTime().ToMS(); if (end_ms > 0) - client_printf(client, "Range: %u.%03u-%u.%03u\n", - start_ms / 1000, - start_ms % 1000, - end_ms / 1000, - end_ms % 1000); + r.Format("Range: %u.%03u-%u.%03u\n", + start_ms / 1000, + start_ms % 1000, + end_ms / 1000, + end_ms % 1000); else if (start_ms > 0) - client_printf(client, "Range: %u.%03u-\n", - start_ms / 1000, - start_ms % 1000); + r.Format("Range: %u.%03u-\n", + start_ms / 1000, + start_ms % 1000); if (song.GetLastModified() > 0) - time_print(client, "Last-Modified", song.GetLastModified()); + time_print(r, "Last-Modified", song.GetLastModified()); - tag_print_values(client, song.GetTag()); + tag_print_values(r, song.GetTag()); const auto duration = song.GetDuration(); if (!duration.IsNegative()) - client_printf(client, "Time: %i\n" - "duration: %1.3f\n", - duration.RoundS(), - duration.ToDoubleS()); + r.Format("Time: %i\n" + "duration: %1.3f\n", + duration.RoundS(), + duration.ToDoubleS()); } diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx index 5b9a507ac..50be70fa8 100644 --- a/src/SongPrint.hxx +++ b/src/SongPrint.hxx @@ -22,18 +22,23 @@ struct LightSong; class DetachedSong; -class Client; +class Response; +struct Partition; void -song_print_info(Client &client, const DetachedSong &song, bool base=false); +song_print_info(Response &r, Partition &partition, + const DetachedSong &song, bool base=false); void -song_print_info(Client &client, const LightSong &song, bool base=false); +song_print_info(Response &r, Partition &partition, + const LightSong &song, bool base=false); void -song_print_uri(Client &client, const LightSong &song, bool base=false); +song_print_uri(Response &r, Partition &partition, + const LightSong &song, bool base=false); void -song_print_uri(Client &client, const DetachedSong &song, bool base=false); +song_print_uri(Response &r, Partition &partition, + const DetachedSong &song, bool base=false); #endif diff --git a/src/Stats.cxx b/src/Stats.cxx index cb33ecad3..9ed3a25dd 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "Stats.hxx" #include "PlayerControl.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "db/Selection.hxx" @@ -94,7 +94,7 @@ stats_update(const Database &db) } static void -db_stats_print(Client &client, const Database &db) +db_stats_print(Response &r, const Database &db) { if (!stats_update(db)) return; @@ -102,41 +102,38 @@ db_stats_print(Client &client, const Database &db) unsigned total_duration_s = std::chrono::duration_cast(stats.total_duration).count(); - client_printf(client, - "artists: %u\n" - "albums: %u\n" - "songs: %u\n" - "db_playtime: %u\n", - stats.artist_count, - stats.album_count, - stats.song_count, - total_duration_s); + r.Format("artists: %u\n" + "albums: %u\n" + "songs: %u\n" + "db_playtime: %u\n", + stats.artist_count, + stats.album_count, + stats.song_count, + total_duration_s); const time_t update_stamp = db.GetUpdateStamp(); if (update_stamp > 0) - client_printf(client, - "db_update: %lu\n", - (unsigned long)update_stamp); + r.Format("db_update: %lu\n", + (unsigned long)update_stamp); } #endif void -stats_print(Client &client) +stats_print(Response &r, const Partition &partition) { - client_printf(client, - "uptime: %u\n" - "playtime: %lu\n", + r.Format("uptime: %u\n" + "playtime: %lu\n", #ifdef WIN32 - GetProcessUptimeS(), + GetProcessUptimeS(), #else - MonotonicClockS() - start_time, + MonotonicClockS() - start_time, #endif - (unsigned long)(client.player_control.GetTotalPlayTime() + 0.5)); + (unsigned long)(partition.pc.GetTotalPlayTime() + 0.5)); #ifdef ENABLE_DATABASE - const Database *db = client.partition.instance.database; + const Database *db = partition.instance.database; if (db != nullptr) - db_stats_print(client, *db); + db_stats_print(r, *db); #endif } diff --git a/src/Stats.hxx b/src/Stats.hxx index c3f56286e..879e1b7be 100644 --- a/src/Stats.hxx +++ b/src/Stats.hxx @@ -20,7 +20,8 @@ #ifndef MPD_STATS_HXX #define MPD_STATS_HXX -class Client; +class Response; +struct Partition; void stats_global_init(); @@ -29,6 +30,6 @@ void stats_invalidate(); void -stats_print(Client &client); +stats_print(Response &r, const Partition &partition); #endif diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx index 39027ba11..331cabda5 100644 --- a/src/TagPrint.cxx +++ b/src/TagPrint.cxx @@ -21,40 +21,40 @@ #include "TagPrint.hxx" #include "tag/Tag.hxx" #include "tag/TagSettings.h" -#include "client/Client.hxx" +#include "client/Response.hxx" -void tag_print_types(Client &client) +void +tag_print_types(Response &r) { int i; for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { if (!ignore_tag_items[i]) - client_printf(client, "tagtype: %s\n", - tag_item_names[i]); + r.Format("tagtype: %s\n", tag_item_names[i]); } } void -tag_print(Client &client, TagType type, const char *value) +tag_print(Response &r, TagType type, const char *value) { - client_printf(client, "%s: %s\n", tag_item_names[type], value); + r.Format("%s: %s\n", tag_item_names[type], value); } void -tag_print_values(Client &client, const Tag &tag) +tag_print_values(Response &r, const Tag &tag) { for (const auto &i : tag) - client_printf(client, "%s: %s\n", - tag_item_names[i.type], i.value); + r.Format("%s: %s\n", tag_item_names[i.type], i.value); } -void tag_print(Client &client, const Tag &tag) +void +tag_print(Response &r, const Tag &tag) { if (!tag.duration.IsNegative()) - client_printf(client, "Time: %i\n" - "duration: %1.3f\n", - tag.duration.RoundS(), - tag.duration.ToDoubleS()); + r.Format("Time: %i\n" + "duration: %1.3f\n", + tag.duration.RoundS(), + tag.duration.ToDoubleS()); - tag_print_values(client, tag); + tag_print_values(r, tag); } diff --git a/src/TagPrint.hxx b/src/TagPrint.hxx index 70cb6323a..30405638e 100644 --- a/src/TagPrint.hxx +++ b/src/TagPrint.hxx @@ -25,17 +25,18 @@ enum TagType : uint8_t; struct Tag; -class Client; - -void tag_print_types(Client &client); +class Response; void -tag_print(Client &client, TagType type, const char *value); +tag_print_types(Response &response); void -tag_print_values(Client &client, const Tag &tag); +tag_print(Response &response, TagType type, const char *value); void -tag_print(Client &client, const Tag &tag); +tag_print_values(Response &response, const Tag &tag); + +void +tag_print(Response &response, const Tag &tag); #endif diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx index 326743869..f9a4dbd94 100644 --- a/src/TimePrint.cxx +++ b/src/TimePrint.cxx @@ -19,10 +19,10 @@ #include "config.h" #include "TimePrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -time_print(Client &client, const char *name, time_t t) +time_print(Response &r, const char *name, time_t t) { #ifdef WIN32 const struct tm *tm2 = gmtime(&t); @@ -41,5 +41,5 @@ time_print(Client &client, const char *name, time_t t) "%FT%TZ", #endif tm2); - client_printf(client, "%s: %s\n", name, buffer); + r.Format("%s: %s\n", name, buffer); } diff --git a/src/TimePrint.hxx b/src/TimePrint.hxx index 8f1f21050..6ded9cca0 100644 --- a/src/TimePrint.hxx +++ b/src/TimePrint.hxx @@ -22,12 +22,12 @@ #include -class Client; +class Response; /** * Write a line with a time stamp to the client. */ void -time_print(Client &client, const char *name, time_t t); +time_print(Response &r, const char *name, time_t t); #endif diff --git a/src/client/Response.cxx b/src/client/Response.cxx new file mode 100644 index 000000000..9beaab156 --- /dev/null +++ b/src/client/Response.cxx @@ -0,0 +1,72 @@ +/* + * 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Response.hxx" +#include "Client.hxx" +#include "protocol/Result.hxx" +#include "util/FormatString.hxx" + +#include + +bool +Response::Write(const void *data, size_t length) +{ + return client.Write(data, length); +} + +bool +Response::Write(const char *data) +{ + return Write(data, strlen(data)); +} + +bool +Response::FormatV(const char *fmt, va_list args) +{ + char *p = FormatNewV(fmt, args); + bool success = Write(p); + delete[] p; + return success; +} + +bool +Response::Format(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool success = FormatV(fmt, args); + va_end(args); + return success; +} + +void +Response::Error(enum ack code, const char *msg) +{ + command_error(client, code, "%s", msg); +} + +void +Response::FormatError(enum ack code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + command_error_v(client, code, fmt, args); + va_end(args); +} diff --git a/src/client/Response.hxx b/src/client/Response.hxx new file mode 100644 index 000000000..ba53298c8 --- /dev/null +++ b/src/client/Response.hxx @@ -0,0 +1,49 @@ +/* + * 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_RESPONSE_HXX +#define MPD_RESPONSE_HXX + +#include "check.h" +#include "protocol/Ack.hxx" + +#include +#include + +class Client; + +class Response { + Client &client; + +public: + explicit Response(Client &_client):client(_client) {} + + Response(const Response &) = delete; + Response &operator=(const Response &) = delete; + + bool Write(const void *data, size_t length); + bool Write(const char *data); + bool FormatV(const char *fmt, va_list args); + bool Format(const char *fmt, ...); + + void Error(enum ack code, const char *msg); + void FormatError(enum ack code, const char *fmt, ...); +}; + +#endif diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 9f0b0a606..57d4db983 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -36,6 +36,7 @@ #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" @@ -226,38 +227,50 @@ command_available(gcc_unused const Partition &partition, return true; } -/* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, gcc_unused Request args) +PrintAvailableCommands(Response &r, const Partition &partition, + unsigned permission) { - const unsigned permission = client.GetPermission(); - for (unsigned i = 0; i < num_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 Request args) +PrintUnavailableCommands(Response &r, unsigned permission) { - const unsigned permission = client.GetPermission(); - for (unsigned i = 0; i < num_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; } +/* don't be fooled, this is the command handler for "commands" command */ +static CommandResult +handle_commands(Client &client, gcc_unused Request args) +{ + Response r(client); + return PrintAvailableCommands(r, client.partition, + client.GetPermission()); +} + +static CommandResult +handle_not_commands(Client &client, gcc_unused Request args) +{ + Response r(client); + return PrintUnavailableCommands(r, client.GetPermission()); +} + void command_init() { @@ -299,7 +312,8 @@ command_check_request(const struct command *cmd, Client &client, unsigned permission, Request args) { if (cmd->permission != (permission & cmd->permission)) { - command_error(client, ACK_ERROR_PERMISSION, + Response r(client); + r.FormatError(ACK_ERROR_PERMISSION, "you don't have permission for \"%s\"", cmd->cmd); return false; @@ -312,16 +326,19 @@ command_check_request(const struct command *cmd, Client &client, return true; if (min == max && unsigned(max) != args.size) { - command_error(client, ACK_ERROR_ARG, + Response r(client); + r.FormatError(ACK_ERROR_ARG, "wrong number of arguments for \"%s\"", cmd->cmd); return false; } else if (args.size < unsigned(min)) { - command_error(client, ACK_ERROR_ARG, + Response r(client); + r.FormatError(ACK_ERROR_ARG, "too few arguments for \"%s\"", cmd->cmd); return false; } else if (max >= 0 && args.size > unsigned(max)) { - command_error(client, ACK_ERROR_ARG, + Response r(client); + r.FormatError(ACK_ERROR_ARG, "too many arguments for \"%s\"", cmd->cmd); return false; } else @@ -336,7 +353,8 @@ command_checked_lookup(Client &client, unsigned permission, const struct command *cmd = command_lookup(cmd_name); if (cmd == nullptr) { - command_error(client, ACK_ERROR_UNKNOWN, + Response r(client); + r.FormatError(ACK_ERROR_UNKNOWN, "unknown command \"%s\"", cmd_name); return nullptr; } @@ -357,7 +375,7 @@ command_process(Client &client, unsigned num, char *line) command_list_num = num; /* get the command name (first word on the line) */ - /* we have to set current_command because command_error() + /* we have to set current_command because Response::Error() expects it to be set */ Tokenizer tokenizer(line); @@ -366,12 +384,12 @@ command_process(Client &client, unsigned num, char *line) tokenizer.NextWord(error); if (cmd_name == nullptr) { current_command = ""; + + Response r(client); 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()); + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); current_command = nullptr; @@ -387,8 +405,8 @@ command_process(Client &client, unsigned num, char *line) while (true) { if (args.size == COMMAND_ARGV_MAX) { - command_error(client, ACK_ERROR_ARG, - "Too many arguments"); + Response r(client); + r.Error(ACK_ERROR_ARG, "Too many arguments"); current_command = nullptr; return CommandResult::ERROR; } @@ -398,7 +416,8 @@ command_process(Client &client, unsigned num, char *line) if (tokenizer.IsEnd()) break; - command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage()); + Response r(client); + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); current_command = nullptr; return CommandResult::ERROR; } diff --git a/src/command/CommandError.cxx b/src/command/CommandError.cxx index da0571295..d95722c3b 100644 --- a/src/command/CommandError.cxx +++ b/src/command/CommandError.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "CommandError.hxx" #include "db/DatabaseError.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -29,57 +29,55 @@ #include CommandResult -print_playlist_result(Client &client, PlaylistResult result) +print_playlist_result(Response &r, PlaylistResult result) { switch (result) { case PlaylistResult::SUCCESS: return CommandResult::OK; case PlaylistResult::ERRNO: - command_error(client, ACK_ERROR_SYSTEM, "%s", - strerror(errno)); + r.Error(ACK_ERROR_SYSTEM, strerror(errno)); return CommandResult::ERROR; case PlaylistResult::DENIED: - command_error(client, ACK_ERROR_PERMISSION, "Access denied"); + r.Error(ACK_ERROR_PERMISSION, "Access denied"); return CommandResult::ERROR; case PlaylistResult::NO_SUCH_SONG: - command_error(client, ACK_ERROR_NO_EXIST, "No such song"); + r.Error(ACK_ERROR_NO_EXIST, "No such song"); return CommandResult::ERROR; case PlaylistResult::NO_SUCH_LIST: - command_error(client, ACK_ERROR_NO_EXIST, "No such playlist"); + r.Error(ACK_ERROR_NO_EXIST, "No such playlist"); return CommandResult::ERROR; case PlaylistResult::LIST_EXISTS: - command_error(client, ACK_ERROR_EXIST, - "Playlist already exists"); + r.Error(ACK_ERROR_EXIST, "Playlist already exists"); return CommandResult::ERROR; case PlaylistResult::BAD_NAME: - command_error(client, ACK_ERROR_ARG, - "playlist name is invalid: " - "playlist names may not contain slashes," - " newlines or carriage returns"); + r.Error(ACK_ERROR_ARG, + "playlist name is invalid: " + "playlist names may not contain slashes," + " newlines or carriage returns"); return CommandResult::ERROR; case PlaylistResult::BAD_RANGE: - command_error(client, ACK_ERROR_ARG, "Bad song index"); + r.Error(ACK_ERROR_ARG, "Bad song index"); return CommandResult::ERROR; case PlaylistResult::NOT_PLAYING: - command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing"); + r.Error(ACK_ERROR_PLAYER_SYNC, "Not playing"); return CommandResult::ERROR; case PlaylistResult::TOO_LARGE: - command_error(client, ACK_ERROR_PLAYLIST_MAX, - "playlist is at the max size"); + r.Error(ACK_ERROR_PLAYLIST_MAX, + "playlist is at the max size"); return CommandResult::ERROR; case PlaylistResult::DISABLED: - command_error(client, ACK_ERROR_UNKNOWN, - "stored playlist support is disabled"); + r.Error(ACK_ERROR_UNKNOWN, + "stored playlist support is disabled"); return CommandResult::ERROR; } @@ -88,42 +86,39 @@ print_playlist_result(Client &client, PlaylistResult result) } CommandResult -print_error(Client &client, const Error &error) +print_error(Response &r, const Error &error) { assert(error.IsDefined()); LogError(error); if (error.IsDomain(playlist_domain)) { - return print_playlist_result(client, + return print_playlist_result(r, PlaylistResult(error.GetCode())); } else if (error.IsDomain(ack_domain)) { - command_error(client, (ack)error.GetCode(), - "%s", error.GetMessage()); + r.Error((ack)error.GetCode(), error.GetMessage()); return CommandResult::ERROR; #ifdef ENABLE_DATABASE } else if (error.IsDomain(db_domain)) { switch ((enum db_error)error.GetCode()) { case DB_DISABLED: - command_error(client, ACK_ERROR_NO_EXIST, "%s", - error.GetMessage()); + r.Error(ACK_ERROR_NO_EXIST, error.GetMessage()); return CommandResult::ERROR; case DB_NOT_FOUND: - command_error(client, ACK_ERROR_NO_EXIST, "Not found"); + r.Error(ACK_ERROR_NO_EXIST, "Not found"); return CommandResult::ERROR; case DB_CONFLICT: - command_error(client, ACK_ERROR_ARG, "Conflict"); + r.Error(ACK_ERROR_ARG, "Conflict"); return CommandResult::ERROR; } #endif } else if (error.IsDomain(errno_domain)) { - command_error(client, ACK_ERROR_SYSTEM, "%s", - strerror(error.GetCode())); + r.Error(ACK_ERROR_SYSTEM, strerror(error.GetCode())); return CommandResult::ERROR; } - command_error(client, ACK_ERROR_UNKNOWN, "error"); + r.Error(ACK_ERROR_UNKNOWN, "error"); return CommandResult::ERROR; } diff --git a/src/command/CommandError.hxx b/src/command/CommandError.hxx index 6c42ed960..e33386078 100644 --- a/src/command/CommandError.hxx +++ b/src/command/CommandError.hxx @@ -23,16 +23,16 @@ #include "CommandResult.hxx" #include "PlaylistError.hxx" -class Client; +class Response; class Error; CommandResult -print_playlist_result(Client &client, PlaylistResult result); +print_playlist_result(Response &r, PlaylistResult result); /** * Send the #Error to the client. */ CommandResult -print_error(Client &client, const Error &error); +print_error(Response &r, const Error &error); #endif diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index 6e6f3399b..62764139e 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -28,11 +28,11 @@ #include "db/Selection.hxx" #include "CommandError.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "tag/Tag.hxx" #include "util/ConstBuffer.hxx" #include "util/Error.hxx" #include "SongFilter.hxx" -#include "protocol/Result.hxx" #include "BulkEdit.hxx" #include @@ -40,11 +40,13 @@ CommandResult handle_listfiles_db(Client &client, const char *uri) { + Response r(client); const DatabaseSelection selection(uri, false); Error error; - if (!db_selection_print(client, selection, false, true, error)) - return print_error(client, error); + if (!db_selection_print(r, client.partition, + selection, false, true, error)) + return print_error(r, error); return CommandResult::OK; } @@ -52,14 +54,17 @@ handle_listfiles_db(Client &client, const char *uri) CommandResult handle_lsinfo2(Client &client, Request args) { + Response r(client); + /* default is root directory */ const auto uri = args.GetOptional(0, ""); const DatabaseSelection selection(uri, false); Error error; - if (!db_selection_print(client, selection, true, false, error)) - return print_error(client, error); + if (!db_selection_print(r, client.partition, + selection, true, false, error)) + return print_error(r, error); return CommandResult::OK; } @@ -67,9 +72,11 @@ handle_lsinfo2(Client &client, Request args) static CommandResult handle_match(Client &client, Request args, bool fold_case) { + Response r(client); + RangeArg window; if (args.size >= 2 && strcmp(args[args.size - 2], "window") == 0) { - if (!args.Parse(args.size - 1, window, client)) + if (!args.Parse(args.size - 1, window, r)) return CommandResult::ERROR; args.pop_back(); @@ -79,17 +86,18 @@ handle_match(Client &client, Request args, bool fold_case) SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } const DatabaseSelection selection("", true, &filter); Error error; - return db_selection_print(client, selection, true, false, + return db_selection_print(r, client.partition, + selection, true, false, window.start, window.end, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult @@ -107,9 +115,11 @@ handle_search(Client &client, Request args) static CommandResult handle_match_add(Client &client, Request args, bool fold_case) { + Response r(client); + SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } @@ -119,7 +129,7 @@ handle_match_add(Client &client, Request args, bool fold_case) Error error; return AddFromDatabase(client.partition, selection, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult @@ -137,34 +147,38 @@ handle_searchadd(Client &client, Request args) CommandResult handle_searchaddpl(Client &client, Request args) { + Response r(client); + const char *playlist = args.shift(); SongFilter filter; if (!filter.Parse(args, true)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } Error error; const Database *db = client.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); return search_add_to_playlist(*db, *client.GetStorage(), "", playlist, &filter, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_count(Client &client, Request args) { + Response r(client); + TagType group = TAG_NUM_OF_ITEM_TYPES; if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) { const char *s = args[args.size - 1]; group = tag_name_parse_i(s); if (group == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", s); return CommandResult::ERROR; } @@ -175,38 +189,43 @@ handle_count(Client &client, Request args) SongFilter filter; if (!args.IsEmpty() && !filter.Parse(args, false)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } Error error; - return PrintSongCount(client, "", &filter, group, error) + return PrintSongCount(r, client.partition, "", &filter, group, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_listall(Client &client, Request args) { + Response r(client); + /* default is root directory */ const auto uri = args.GetOptional(0, ""); Error error; - return db_selection_print(client, DatabaseSelection(uri, true), + return db_selection_print(r, client.partition, + DatabaseSelection(uri, true), false, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_list(Client &client, Request args) { + Response r(client); + const char *tag_name = args.shift(); unsigned tagType = locate_parse_type(tag_name); if (tagType >= TAG_NUM_OF_ITEM_TYPES && tagType != LOCATE_TAG_FILE_TYPE) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } @@ -217,7 +236,7 @@ handle_list(Client &client, Request args) if (args.size == 1) { /* for compatibility with < 0.12.0 */ if (tagType != TAG_ALBUM) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "should be \"%s\" for 3 arguments", tag_item_names[TAG_ALBUM]); return CommandResult::ERROR; @@ -231,7 +250,7 @@ handle_list(Client &client, Request args) const char *s = args[args.size - 1]; TagType gt = tag_name_parse_i(s); if (gt == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", s); return CommandResult::ERROR; } @@ -246,8 +265,7 @@ handle_list(Client &client, Request args) filter = new SongFilter(); if (!filter->Parse(args, false)) { delete filter; - command_error(client, ACK_ERROR_ARG, - "not able to parse args"); + r.Error(ACK_ERROR_ARG, "not able to parse args"); return CommandResult::ERROR; } } @@ -255,15 +273,16 @@ handle_list(Client &client, Request args) if (tagType < TAG_NUM_OF_ITEM_TYPES && group_mask & (1u << tagType)) { delete filter; - command_error(client, ACK_ERROR_ARG, "Conflicting group"); + r.Error(ACK_ERROR_ARG, "Conflicting group"); return CommandResult::ERROR; } Error error; CommandResult ret = - PrintUniqueTags(client, tagType, group_mask, filter, error) + PrintUniqueTags(r, client.partition, + tagType, group_mask, filter, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); delete filter; @@ -273,12 +292,15 @@ handle_list(Client &client, Request args) CommandResult handle_listallinfo(Client &client, Request args) { + Response r(client); + /* default is root directory */ const auto uri = args.GetOptional(0, ""); Error error; - return db_selection_print(client, DatabaseSelection(uri, true), + return db_selection_print(r, client.partition, + DatabaseSelection(uri, true), true, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 574254d7a..c30dad8c8 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -24,8 +24,8 @@ #include "Request.hxx" #include "CommandError.hxx" #include "protocol/Ack.hxx" -#include "protocol/Result.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "util/ConstBuffer.hxx" #include "util/CharUtil.hxx" #include "util/UriUtil.hxx" @@ -72,21 +72,22 @@ skip_path(Path name_fs) CommandResult handle_listfiles_local(Client &client, const char *path_utf8) { + Response r(client); + const auto path_fs = AllocatedPath::FromUTF8(path_utf8); if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); + r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); return CommandResult::ERROR; } Error error; if (!client.AllowFile(path_fs, error)) - return print_error(client, error); + return print_error(r, error); DirectoryReader reader(path_fs); if (reader.HasFailed()) { error.FormatErrno("Failed to open '%s'", path_utf8); - return print_error(client, error); + return print_error(r, error); } while (reader.ReadEntry()) { @@ -105,17 +106,16 @@ handle_listfiles_local(Client &client, const char *path_utf8) continue; if (fi.IsRegular()) - client_printf(client, "file: %s\n" - "size: %" PRIu64 "\n", - name_utf8.c_str(), - fi.GetSize()); + r.Format("file: %s\n" + "size: %" PRIu64 "\n", + name_utf8.c_str(), + fi.GetSize()); else if (fi.IsDirectory()) - client_printf(client, "directory: %s\n", - name_utf8.c_str()); + r.Format("directory: %s\n", name_utf8.c_str()); else continue; - time_print(client, "Last-Modified", fi.GetModificationTime()); + time_print(r, "Last-Modified", fi.GetModificationTime()); } return CommandResult::OK; @@ -158,10 +158,10 @@ IsValidValue(const char *p) static void print_pair(const char *key, const char *value, void *ctx) { - Client &client = *(Client *)ctx; + auto &r = *(Response *)ctx; if (IsValidName(key) && IsValidValue(value)) - client_printf(client, "%s: %s\n", key, value); + r.Format("%s: %s\n", key, value); } static constexpr tag_handler print_comment_handler = { @@ -171,17 +171,15 @@ static constexpr tag_handler print_comment_handler = { }; static CommandResult -read_stream_comments(Client &client, const char *uri) +read_stream_comments(Response &r, const char *uri) { if (!uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); + r.Error(ACK_ERROR_NO_EXIST, "unsupported URI scheme"); return CommandResult::ERROR; } - if (!tag_stream_scan(uri, print_comment_handler, &client)) { - command_error(client, ACK_ERROR_NO_EXIST, - "Failed to load file"); + if (!tag_stream_scan(uri, print_comment_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "Failed to load file"); return CommandResult::ERROR; } @@ -190,16 +188,15 @@ read_stream_comments(Client &client, const char *uri) } static CommandResult -read_file_comments(Client &client, const Path path_fs) +read_file_comments(Response &r, const Path path_fs) { - if (!tag_file_scan(path_fs, print_comment_handler, &client)) { - command_error(client, ACK_ERROR_NO_EXIST, - "Failed to load file"); + if (!tag_file_scan(path_fs, print_comment_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "Failed to load file"); return CommandResult::ERROR; } - tag_ape_scan2(path_fs, &print_comment_handler, &client); - tag_id3_scan(path_fs, &print_comment_handler, &client); + tag_ape_scan2(path_fs, &print_comment_handler, &r); + tag_id3_scan(path_fs, &print_comment_handler, &r); return CommandResult::OK; @@ -219,6 +216,8 @@ translate_uri(const char *uri) CommandResult handle_read_comments(Client &client, Request args) { + Response r(client); + assert(args.size == 1); const char *const uri = translate_uri(args.front()); @@ -227,25 +226,23 @@ handle_read_comments(Client &client, Request args) const char *path_utf8 = uri + 7; AllocatedPath path_fs = AllocatedPath::FromUTF8(path_utf8); if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); + r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); return CommandResult::ERROR; } Error error; if (!client.AllowFile(path_fs, error)) - return print_error(client, error); + return print_error(r, error); - return read_file_comments(client, path_fs); + return read_file_comments(r, path_fs); } else if (uri_has_scheme(uri)) { - return read_stream_comments(client, uri); + return read_stream_comments(r, uri); } else if (!PathTraitsUTF8::IsAbsolute(uri)) { #ifdef ENABLE_DATABASE const Storage *storage = client.GetStorage(); if (storage == nullptr) { #endif - command_error(client, ACK_ERROR_NO_EXIST, - "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; #ifdef ENABLE_DATABASE } @@ -253,21 +250,20 @@ handle_read_comments(Client &client, Request args) { AllocatedPath path_fs = storage->MapFS(uri); if (!path_fs.IsNull()) - return read_file_comments(client, path_fs); + return read_file_comments(r, path_fs); } { const std::string uri2 = storage->MapUTF8(uri); if (uri_has_scheme(uri2.c_str())) - return read_stream_comments(client, - uri2.c_str()); + return read_stream_comments(r, uri2.c_str()); } - command_error(client, ACK_ERROR_NO_EXIST, "No such file"); + r.Error(ACK_ERROR_NO_EXIST, "No such file"); return CommandResult::ERROR; #endif } else { - command_error(client, ACK_ERROR_NO_EXIST, "No such file"); + r.Error(ACK_ERROR_NO_EXIST, "No such file"); return CommandResult::ERROR; } } diff --git a/src/command/MessageCommands.cxx b/src/command/MessageCommands.cxx index 24c9ddf2d..62d47ff0e 100644 --- a/src/command/MessageCommands.cxx +++ b/src/command/MessageCommands.cxx @@ -22,9 +22,9 @@ #include "Request.hxx" #include "client/Client.hxx" #include "client/ClientList.hxx" +#include "client/Response.hxx" #include "Instance.hxx" #include "Partition.hxx" -#include "protocol/Result.hxx" #include "util/ConstBuffer.hxx" #include @@ -35,6 +35,8 @@ CommandResult handle_subscribe(Client &client, Request args) { + Response r(client); + assert(args.size == 1); const char *const channel_name = args[0]; @@ -43,18 +45,15 @@ handle_subscribe(Client &client, Request args) return CommandResult::OK; case Client::SubscribeResult::INVALID: - command_error(client, ACK_ERROR_ARG, - "invalid channel name"); + r.Error(ACK_ERROR_ARG, "invalid channel name"); return CommandResult::ERROR; case Client::SubscribeResult::ALREADY: - command_error(client, ACK_ERROR_EXIST, - "already subscribed to this channel"); + r.Error(ACK_ERROR_EXIST, "already subscribed to this channel"); return CommandResult::ERROR; case Client::SubscribeResult::FULL: - command_error(client, ACK_ERROR_EXIST, - "subscription list is full"); + r.Error(ACK_ERROR_EXIST, "subscription list is full"); return CommandResult::ERROR; } @@ -66,14 +65,15 @@ handle_subscribe(Client &client, Request args) CommandResult handle_unsubscribe(Client &client, Request args) { + Response r(client); + assert(args.size == 1); const char *const channel_name = args[0]; if (client.Unsubscribe(channel_name)) return CommandResult::OK; else { - command_error(client, ACK_ERROR_NO_EXIST, - "not subscribed to this channel"); + r.Error(ACK_ERROR_NO_EXIST, "not subscribed to this channel"); return CommandResult::ERROR; } } @@ -88,8 +88,9 @@ handle_channels(Client &client, gcc_unused Request args) channels.insert(c.subscriptions.begin(), c.subscriptions.end()); + Response r(client); for (const auto &channel : channels) - client_printf(client, "channel: %s\n", channel.c_str()); + r.Format("channel: %s\n", channel.c_str()); return CommandResult::OK; } @@ -100,11 +101,12 @@ handle_read_messages(Client &client, { assert(args.IsEmpty()); + Response r(client); while (!client.messages.empty()) { const ClientMessage &msg = client.messages.front(); - client_printf(client, "channel: %s\nmessage: %s\n", - msg.GetChannel(), msg.GetMessage()); + r.Format("channel: %s\nmessage: %s\n", + msg.GetChannel(), msg.GetMessage()); client.messages.pop_front(); } @@ -119,9 +121,9 @@ handle_send_message(Client &client, Request args) const char *const channel_name = args[0]; const char *const message_text = args[1]; + Response r(client); if (!client_message_valid_channel_name(channel_name)) { - command_error(client, ACK_ERROR_ARG, - "invalid channel name"); + r.Error(ACK_ERROR_ARG, "invalid channel name"); return CommandResult::ERROR; } @@ -134,8 +136,8 @@ handle_send_message(Client &client, Request args) if (sent) return CommandResult::OK; else { - command_error(client, ACK_ERROR_NO_EXIST, - "nobody is subscribed to this channel"); + r.Error(ACK_ERROR_NO_EXIST, + "nobody is subscribed to this channel"); return CommandResult::ERROR; } } diff --git a/src/command/NeighborCommands.cxx b/src/command/NeighborCommands.cxx index 10dbe3074..d1b2ec7c7 100644 --- a/src/command/NeighborCommands.cxx +++ b/src/command/NeighborCommands.cxx @@ -21,9 +21,9 @@ #include "NeighborCommands.hxx" #include "Request.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Instance.hxx" #include "Partition.hxx" -#include "protocol/Result.hxx" #include "neighbor/Glue.hxx" #include "neighbor/Info.hxx" #include "util/ConstBuffer.hxx" @@ -42,19 +42,19 @@ neighbor_commands_available(const Instance &instance) CommandResult handle_listneighbors(Client &client, gcc_unused Request args) { + Response r(client); + const NeighborGlue *const neighbors = client.partition.instance.neighbors; if (neighbors == nullptr) { - command_error(client, ACK_ERROR_UNKNOWN, - "No neighbor plugin configured"); + r.Error(ACK_ERROR_UNKNOWN, "No neighbor plugin configured"); return CommandResult::ERROR; } for (const auto &i : neighbors->GetList()) - client_printf(client, - "neighbor: %s\n" - "name: %s\n", - i.uri.c_str(), - i.display_name.c_str()); + r.Format("neighbor: %s\n" + "name: %s\n", + i.uri.c_str(), + i.display_name.c_str()); return CommandResult::OK; } diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index 2e20d8dd5..c1346ca44 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -32,7 +32,6 @@ #include "tag/TagHandler.hxx" #include "TimePrint.hxx" #include "decoder/DecoderPrint.hxx" -#include "protocol/Result.hxx" #include "ls.hxx" #include "mixer/Volume.hxx" #include "util/UriUtil.hxx" @@ -44,6 +43,7 @@ #include "PlaylistFile.hxx" #include "db/PlaylistVector.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "Idle.hxx" @@ -58,36 +58,39 @@ #include 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 Request args) { + Response r(client); 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 Request args) { - decoder_list_print(client); + Response r(client); + decoder_list_print(r); return CommandResult::OK; } CommandResult handle_tagtypes(Client &client, gcc_unused Request args) { - tag_print_types(client); + Response r(client); + tag_print_types(r); return CommandResult::OK; } @@ -106,14 +109,16 @@ handle_close(gcc_unused Client &client, gcc_unused Request args) 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, Request args) { + Response r(client); + /* default is root directory */ const auto uri = args.GetOptional(0, ""); @@ -124,7 +129,7 @@ handle_listfiles(Client &client, Request args) #ifdef ENABLE_DATABASE if (uri_has_scheme(uri)) /* use storage plugin to list remote directory */ - return handle_listfiles_storage(client, uri); + return handle_listfiles_storage(r, uri); /* must be a path relative to the configured music_directory */ @@ -132,14 +137,14 @@ handle_listfiles(Client &client, Request args) if (client.partition.instance.storage != nullptr) /* if we have a storage instance, obtain a list of files from it */ - return handle_listfiles_storage(client, + 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, uri); #else - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; #endif } @@ -156,42 +161,40 @@ handle_lsinfo(Client &client, Request args) /* default is root directory */ const auto uri = args.GetOptional(0, ""); + Response r(client); + 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"); + r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); return CommandResult::ERROR; } Error error; if (!client.AllowFile(path_fs, error)) - return print_error(client, error); + return print_error(r, error); DetachedSong song(path_utf8); if (!song.Update()) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such file"); + r.Error(ACK_ERROR_NO_EXIST, "No such file"); return CommandResult::ERROR; } - song_print_info(client, song); + song_print_info(r, client.partition, song); return CommandResult::OK; } if (uri_has_scheme(uri)) { if (!uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); + r.Error(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"); + if (!tag_stream_scan(uri, print_tag_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "No such file"); return CommandResult::ERROR; } @@ -207,10 +210,10 @@ handle_lsinfo(Client &client, Request args) 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 } @@ -224,13 +227,14 @@ static CommandResult handle_update(Client &client, UpdateService &update, const char *uri_utf8, bool discard) { + Response r(client); + 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; } } @@ -239,17 +243,19 @@ static CommandResult handle_update(Client &client, Database &db, const char *uri_utf8, bool discard) { + Response r(client); + 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; } } @@ -259,6 +265,8 @@ handle_update(Client &client, Database &db, static CommandResult handle_update(Client &client, Request args, bool discard) { + Response r(client); + #ifdef ENABLE_DATABASE const char *path = ""; @@ -270,8 +278,7 @@ handle_update(Client &client, Request args, bool discard) /* 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; } } @@ -288,7 +295,7 @@ handle_update(Client &client, Request args, bool discard) (void)discard; #endif - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } @@ -307,13 +314,14 @@ handle_rescan(Client &client, gcc_unused Request args) CommandResult handle_setvol(Client &client, Request args) { + Response r(client); + unsigned level; - if (!args.Parse(0, level, client, 100)) + if (!args.Parse(0, level, r, 100)) return CommandResult::ERROR; if (!volume_level_change(client.partition.outputs, level)) { - command_error(client, ACK_ERROR_SYSTEM, - "problems setting volume"); + r.Error(ACK_ERROR_SYSTEM, "problems setting volume"); return CommandResult::ERROR; } @@ -323,13 +331,15 @@ handle_setvol(Client &client, Request args) CommandResult handle_volume(Client &client, Request args) { + Response r(client); + int relative; - if (!args.Parse(0, relative, client, -100, 100)) + if (!args.Parse(0, relative, r, -100, 100)) 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; } @@ -341,8 +351,7 @@ handle_volume(Client &client, Request args) 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; } @@ -352,7 +361,8 @@ handle_volume(Client &client, Request args) CommandResult handle_stats(Client &client, gcc_unused Request args) { - stats_print(client); + Response r(client); + stats_print(r, client.partition); return CommandResult::OK; } @@ -365,10 +375,11 @@ handle_ping(gcc_unused Client &client, gcc_unused Request args) CommandResult handle_password(Client &client, Request args) { - unsigned permission = 0; + Response r(client); + unsigned permission = 0; if (getPermissionFromPassword(args.front(), &permission) < 0) { - command_error(client, ACK_ERROR_PASSWORD, "incorrect password"); + r.Error(ACK_ERROR_PASSWORD, "incorrect password"); return CommandResult::ERROR; } @@ -380,9 +391,11 @@ handle_password(Client &client, Request args) CommandResult handle_config(Client &client, gcc_unused Request args) { + Response r(client); + 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; } @@ -390,7 +403,7 @@ handle_config(Client &client, gcc_unused Request args) 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 @@ -400,14 +413,14 @@ handle_config(Client &client, gcc_unused Request args) CommandResult handle_idle(Client &client, Request args) { - unsigned flags = 0; + Response r(client); + unsigned flags = 0; for (const char *i : args) { unsigned event = idle_parse_name(i); if (event == 0) { - command_error(client, ACK_ERROR_ARG, - "Unrecognized idle event: %s", - i); + r.FormatError(ACK_ERROR_ARG, + "Unrecognized idle event: %s", i); return CommandResult::ERROR; } diff --git a/src/command/OutputCommands.cxx b/src/command/OutputCommands.cxx index 39602aba8..472516860 100644 --- a/src/command/OutputCommands.cxx +++ b/src/command/OutputCommands.cxx @@ -22,23 +22,23 @@ #include "Request.hxx" #include "output/OutputPrint.hxx" #include "output/OutputCommand.hxx" -#include "protocol/Result.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "util/ConstBuffer.hxx" CommandResult handle_enableoutput(Client &client, Request args) { - assert(args.size == 1); + Response r(client); + assert(args.size == 1); unsigned device; - if (!args.Parse(0, device, client)) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_enable_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -48,15 +48,15 @@ handle_enableoutput(Client &client, Request args) CommandResult handle_disableoutput(Client &client, Request args) { - assert(args.size == 1); + Response r(client); + assert(args.size == 1); unsigned device; - if (!args.Parse(0, device, client)) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_disable_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -66,15 +66,15 @@ handle_disableoutput(Client &client, Request args) CommandResult handle_toggleoutput(Client &client, Request args) { - assert(args.size == 1); + Response r(client); + assert(args.size == 1); unsigned device; - if (!args.Parse(0, device, client)) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_toggle_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -86,7 +86,8 @@ handle_devices(Client &client, gcc_unused Request args) { assert(args.IsEmpty()); - printAudioDevices(client, client.partition.outputs); + Response r(client); + printAudioDevices(r, client.partition.outputs); return CommandResult::OK; } diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index e0c0f61a8..3e58cecb1 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -24,10 +24,10 @@ #include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "mixer/Volume.hxx" #include "Partition.hxx" #include "Instance.hxx" -#include "protocol/Result.hxx" #include "AudioFormat.hxx" #include "ReplayGainConfig.hxx" #include "util/ConstBuffer.hxx" @@ -59,23 +59,27 @@ CommandResult handle_play(Client &client, Request args) { + Response r(client); + int song = -1; - if (!args.ParseOptional(0, song, client)) + if (!args.ParseOptional(0, song, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.PlayPosition(song); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_playid(Client &client, Request args) { + Response r(client); + int id = -1; - if (!args.ParseOptional(0, id, client)) + if (!args.ParseOptional(0, id, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.PlayId(id); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult @@ -88,16 +92,19 @@ handle_stop(Client &client, gcc_unused Request args) CommandResult handle_currentsong(Client &client, gcc_unused Request args) { - playlist_print_current(client, client.playlist); + Response r(client); + playlist_print_current(r, client.partition, client.playlist); return CommandResult::OK; } CommandResult handle_pause(Client &client, Request args) { + Response r(client); + if (!args.IsEmpty()) { bool pause_flag; - if (!args.Parse(0, pause_flag, client)) + if (!args.Parse(0, pause_flag, r)) return CommandResult::ERROR; client.player_control.SetPause(pause_flag); @@ -127,68 +134,64 @@ handle_status(Client &client, gcc_unused Request args) break; } + Response r(client); + const playlist &playlist = client.playlist; - client_printf(client, - "volume: %i\n" - COMMAND_STATUS_REPEAT ": %i\n" - COMMAND_STATUS_RANDOM ": %i\n" - COMMAND_STATUS_SINGLE ": %i\n" - COMMAND_STATUS_CONSUME ": %i\n" - COMMAND_STATUS_PLAYLIST ": %li\n" - COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n" - COMMAND_STATUS_MIXRAMPDB ": %f\n" - COMMAND_STATUS_STATE ": %s\n", - volume_level_get(client.partition.outputs), - playlist.GetRepeat(), - playlist.GetRandom(), - playlist.GetSingle(), - playlist.GetConsume(), - (unsigned long)playlist.GetVersion(), - playlist.GetLength(), - client.player_control.GetMixRampDb(), - state); + r.Format("volume: %i\n" + COMMAND_STATUS_REPEAT ": %i\n" + COMMAND_STATUS_RANDOM ": %i\n" + COMMAND_STATUS_SINGLE ": %i\n" + COMMAND_STATUS_CONSUME ": %i\n" + COMMAND_STATUS_PLAYLIST ": %li\n" + COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n" + COMMAND_STATUS_MIXRAMPDB ": %f\n" + COMMAND_STATUS_STATE ": %s\n", + volume_level_get(client.partition.outputs), + playlist.GetRepeat(), + playlist.GetRandom(), + playlist.GetSingle(), + playlist.GetConsume(), + (unsigned long)playlist.GetVersion(), + playlist.GetLength(), + client.player_control.GetMixRampDb(), + state); if (client.player_control.GetCrossFade() > 0) - client_printf(client, - COMMAND_STATUS_CROSSFADE ": %i\n", - int(client.player_control.GetCrossFade() + 0.5)); + r.Format(COMMAND_STATUS_CROSSFADE ": %i\n", + int(client.player_control.GetCrossFade() + 0.5)); if (client.player_control.GetMixRampDelay() > 0) - client_printf(client, - COMMAND_STATUS_MIXRAMPDELAY ": %f\n", - client.player_control.GetMixRampDelay()); + r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n", + client.player_control.GetMixRampDelay()); song = playlist.GetCurrentPosition(); if (song >= 0) { - client_printf(client, - COMMAND_STATUS_SONG ": %i\n" - COMMAND_STATUS_SONGID ": %u\n", - song, playlist.PositionToId(song)); + r.Format(COMMAND_STATUS_SONG ": %i\n" + COMMAND_STATUS_SONGID ": %u\n", + song, playlist.PositionToId(song)); } if (player_status.state != PlayerState::STOP) { - client_printf(client, - COMMAND_STATUS_TIME ": %i:%i\n" - "elapsed: %1.3f\n" - COMMAND_STATUS_BITRATE ": %u\n", - player_status.elapsed_time.RoundS(), - player_status.total_time.IsNegative() - ? 0u - : unsigned(player_status.total_time.RoundS()), - player_status.elapsed_time.ToDoubleS(), - player_status.bit_rate); + r.Format(COMMAND_STATUS_TIME ": %i:%i\n" + "elapsed: %1.3f\n" + COMMAND_STATUS_BITRATE ": %u\n", + player_status.elapsed_time.RoundS(), + player_status.total_time.IsNegative() + ? 0u + : unsigned(player_status.total_time.RoundS()), + player_status.elapsed_time.ToDoubleS(), + player_status.bit_rate); if (!player_status.total_time.IsNegative()) - client_printf(client, "duration: %1.3f\n", - player_status.total_time.ToDoubleS()); + r.Format("duration: %1.3f\n", + player_status.total_time.ToDoubleS()); if (player_status.audio_format.IsDefined()) { struct audio_format_string af_string; - client_printf(client, - COMMAND_STATUS_AUDIO ": %s\n", - audio_format_to_string(player_status.audio_format, - &af_string)); + r.Format(COMMAND_STATUS_AUDIO ": %s\n", + audio_format_to_string(player_status.audio_format, + &af_string)); } } @@ -198,25 +201,21 @@ handle_status(Client &client, gcc_unused Request args) ? update_service->GetId() : 0; if (updateJobId != 0) { - client_printf(client, - COMMAND_STATUS_UPDATING_DB ": %i\n", - updateJobId); + r.Format(COMMAND_STATUS_UPDATING_DB ": %i\n", + updateJobId); } #endif Error error = client.player_control.LockGetError(); if (error.IsDefined()) - client_printf(client, - COMMAND_STATUS_ERROR ": %s\n", - error.GetMessage()); + r.Format(COMMAND_STATUS_ERROR ": %s\n", + error.GetMessage()); song = playlist.GetNextPosition(); - if (song >= 0) { - client_printf(client, - COMMAND_STATUS_NEXTSONG ": %i\n" - COMMAND_STATUS_NEXTSONGID ": %u\n", - song, playlist.PositionToId(song)); - } + if (song >= 0) + r.Format(COMMAND_STATUS_NEXTSONG ": %i\n" + COMMAND_STATUS_NEXTSONGID ": %u\n", + song, playlist.PositionToId(song)); return CommandResult::OK; } @@ -247,8 +246,10 @@ handle_previous(Client &client, gcc_unused Request args) CommandResult handle_repeat(Client &client, Request args) { + Response r(client); + bool status; - if (!args.Parse(0, status, client)) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetRepeat(status); @@ -258,8 +259,10 @@ handle_repeat(Client &client, Request args) CommandResult handle_single(Client &client, Request args) { + Response r(client); + bool status; - if (!args.Parse(0, status, client)) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetSingle(status); @@ -269,8 +272,10 @@ handle_single(Client &client, Request args) CommandResult handle_consume(Client &client, Request args) { + Response r(client); + bool status; - if (!args.Parse(0, status, client)) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetConsume(status); @@ -280,8 +285,10 @@ handle_consume(Client &client, Request args) CommandResult handle_random(Client &client, Request args) { + Response r(client); + bool status; - if (!args.Parse(0, status, client)) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetRandom(status); @@ -299,53 +306,58 @@ handle_clearerror(gcc_unused Client &client, gcc_unused Request args) CommandResult handle_seek(Client &client, Request args) { + Response r(client); + unsigned song; SongTime seek_time; - - if (!args.Parse(0, song, client)) - return CommandResult::ERROR; - if (!args.Parse(1, seek_time, client)) + if (!args.Parse(0, song, r) || !args.Parse(1, seek_time, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekSongPosition(song, seek_time); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_seekid(Client &client, Request args) { + Response r(client); + unsigned id; SongTime seek_time; - if (!args.Parse(0, id, client)) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; - if (!args.Parse(1, seek_time, client)) + if (!args.Parse(1, seek_time, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekSongId(id, seek_time); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_seekcur(Client &client, Request args) { + Response r(client); + const char *p = args.front(); bool relative = *p == '+' || *p == '-'; SignedSongTime seek_time; - if (!ParseCommandArg(client, seek_time, p)) + if (!ParseCommandArg(r, seek_time, p)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekCurrent(seek_time, relative); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_crossfade(Client &client, Request args) { + Response r(client); + unsigned xfade_time; - if (!args.Parse(0, xfade_time, client)) + if (!args.Parse(0, xfade_time, r)) return CommandResult::ERROR; client.player_control.SetCrossFade(xfade_time); @@ -355,8 +367,10 @@ handle_crossfade(Client &client, Request args) CommandResult handle_mixrampdb(Client &client, Request args) { + Response r(client); + float db; - if (!args.Parse(0, db, client)) + if (!args.Parse(0, db, r)) return CommandResult::ERROR; client.player_control.SetMixRampDb(db); @@ -366,8 +380,10 @@ handle_mixrampdb(Client &client, Request args) CommandResult handle_mixrampdelay(Client &client, Request args) { + Response r(client); + float delay_secs; - if (!args.Parse(0, delay_secs, client)) + if (!args.Parse(0, delay_secs, r)) return CommandResult::ERROR; client.player_control.SetMixRampDelay(delay_secs); @@ -378,9 +394,10 @@ handle_mixrampdelay(Client &client, Request args) CommandResult handle_replay_gain_mode(Client &client, Request args) { + Response r(client); + if (!replay_gain_set_mode_string(args.front())) { - command_error(client, ACK_ERROR_ARG, - "Unrecognized replay gain mode"); + r.Error(ACK_ERROR_ARG, "Unrecognized replay gain mode"); return CommandResult::ERROR; } @@ -391,7 +408,7 @@ handle_replay_gain_mode(Client &client, Request args) CommandResult handle_replay_gain_status(Client &client, gcc_unused Request args) { - client_printf(client, "replay_gain_mode: %s\n", - replay_gain_get_mode_string()); + Response r(client); + r.Format("replay_gain_mode: %s\n", replay_gain_get_mode_string()); return CommandResult::OK; } diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index 0122cecbd..1e075e088 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -33,7 +33,7 @@ #include "queue/Playlist.hxx" #include "TimePrint.hxx" #include "client/Client.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "ls.hxx" #include "Mapper.hxx" #include "fs/AllocatedPath.hxx" @@ -48,30 +48,33 @@ playlist_commands_available() } 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_save(Client &client, Request args) { + Response r(client); Error error; return spl_save_playlist(args.front(), client.playlist, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_load(Client &client, Request args) { + Response r(client); + RangeArg range = RangeArg::All(); - if (!args.ParseOptional(1, range, client)) + if (!args.ParseOptional(1, range, r)) return CommandResult::ERROR; const ScopeBulkEdit bulk_edit(client.partition); @@ -82,7 +85,7 @@ handle_load(Client &client, Request args) range.start, range.end, client.playlist, client.player_control, loader, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } @@ -90,97 +93,114 @@ handle_load(Client &client, Request args) CommandResult handle_listplaylist(Client &client, Request args) { + Response r(client); + const char *const name = args.front(); - if (playlist_file_print(client, name, false)) + if (playlist_file_print(r, client.partition, SongLoader(client), + name, false)) return CommandResult::OK; Error error; - return spl_print(client, name, false, error) + return spl_print(r, client.partition, name, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_listplaylistinfo(Client &client, Request args) { + Response r(client); + const char *const name = args.front(); - if (playlist_file_print(client, name, true)) + if (playlist_file_print(r, client.partition, SongLoader(client), + name, true)) return CommandResult::OK; Error error; - return spl_print(client, name, true, error) + return spl_print(r, client.partition, name, true, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_rm(Client &client, Request args) { + Response r(client); + const char *const name = args.front(); Error error; return spl_delete(name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_rename(Client &client, Request args) { + Response r(client); + const char *const old_name = args[0]; const char *const new_name = args[1]; Error error; return spl_rename(old_name, new_name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_playlistdelete(Client &client, Request args) { + Response r(client); + const char *const name = args[0]; unsigned from; - if (!args.Parse(1, from, client)) + if (!args.Parse(1, from, r)) return CommandResult::ERROR; Error error; return spl_remove_index(name, from, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_playlistmove(Client &client, Request args) { + Response r(client); + const char *const name = args.front(); unsigned from, to; - if (!args.Parse(1, from, client) || - !args.Parse(2, to, client)) + if (!args.Parse(1, from, r) || !args.Parse(2, to, r)) return CommandResult::ERROR; Error error; return spl_move_index(name, from, to, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_playlistclear(Client &client, Request args) { + Response r(client); + const char *const name = args.front(); Error error; return spl_clear(name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult handle_playlistadd(Client &client, Request args) { + Response r(client); + const char *const playlist = args[0]; const char *const uri = args[1]; @@ -193,7 +213,7 @@ handle_playlistadd(Client &client, Request args) #ifdef ENABLE_DATABASE const Database *db = client.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); success = search_add_to_playlist(*db, *client.GetStorage(), uri, playlist, nullptr, @@ -204,22 +224,23 @@ handle_playlistadd(Client &client, Request args) } if (!success && !error.IsDefined()) { - command_error(client, ACK_ERROR_NO_EXIST, - "directory or file not found"); + r.Error(ACK_ERROR_NO_EXIST, "directory or file not found"); return CommandResult::ERROR; } - return success ? CommandResult::OK : print_error(client, error); + return success ? CommandResult::OK : print_error(r, error); } CommandResult handle_listplaylists(Client &client, gcc_unused Request args) { + Response r(client); + Error error; const auto list = ListPlaylistFiles(error); if (list.empty() && error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - print_spl_list(client, list); + print_spl_list(r, list); return CommandResult::OK; } diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index 1413a68c9..850631ded 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -28,9 +28,9 @@ #include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "BulkEdit.hxx" -#include "protocol/Result.hxx" #include "ls.hxx" #include "util/ConstBuffer.hxx" #include "util/UriUtil.hxx" @@ -56,6 +56,8 @@ translate_uri(const char *uri) CommandResult handle_add(Client &client, Request args) { + Response r(client); + const char *uri = args.front(); if (memcmp(uri, "/", 2) == 0) /* this URI is malformed, but some clients are buggy @@ -72,7 +74,7 @@ handle_add(Client &client, Request args) Error error; unsigned id = client.partition.AppendURI(loader, uri, error); if (id == 0) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } @@ -84,9 +86,9 @@ handle_add(Client &client, Request args) Error error; return AddFromDatabase(client.partition, selection, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); #else - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; #endif } @@ -94,29 +96,31 @@ handle_add(Client &client, Request args) CommandResult handle_addid(Client &client, Request args) { + Response r(client); + const char *const uri = translate_uri(args.front()); const SongLoader loader(client); Error error; unsigned added_id = client.partition.AppendURI(loader, uri, error); if (added_id == 0) - return print_error(client, error); + return print_error(r, error); if (args.size == 2) { unsigned to; - if (!args.Parse(1, to, client)) + if (!args.Parse(1, to, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.MoveId(added_id, to); if (result != PlaylistResult::SUCCESS) { CommandResult ret = - print_playlist_result(client, result); + print_playlist_result(r, result); client.partition.DeleteId(added_id); return ret; } } - client_printf(client, "Id: %u\n", added_id); + r.Format("Id: %u\n", added_id); return CommandResult::OK; } @@ -154,13 +158,15 @@ parse_time_range(const char *p, SongTime &start_r, SongTime &end_r) CommandResult handle_rangeid(Client &client, Request args) { + Response r(client); + unsigned id; - if (!args.Parse(0, id, client)) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; SongTime start, end; if (!parse_time_range(args[1], start, end)) { - command_error(client, ACK_ERROR_ARG, "Bad range"); + r.Error(ACK_ERROR_ARG, "Bad range"); return CommandResult::ERROR; } @@ -168,7 +174,7 @@ handle_rangeid(Client &client, Request args) if (!client.partition.playlist.SetSongIdRange(client.partition.pc, id, start, end, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } @@ -176,37 +182,44 @@ handle_rangeid(Client &client, Request args) CommandResult handle_delete(Client &client, Request args) { + Response r(client); + RangeArg range; - if (!args.Parse(0, range, client)) + if (!args.Parse(0, range, r)) return CommandResult::ERROR; auto result = client.partition.DeleteRange(range.start, range.end); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_deleteid(Client &client, Request args) { + Response r(client); + unsigned id; - if (!args.Parse(0, id, client)) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.DeleteId(id); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_playlist(Client &client, gcc_unused Request args) { - playlist_print_uris(client, client.playlist); + Response r(client); + playlist_print_uris(r, client.partition, client.playlist); return CommandResult::OK; } CommandResult handle_shuffle(gcc_unused Client &client, Request args) { + Response r(client); + RangeArg range = RangeArg::All(); - if (!args.ParseOptional(0, range, client)) + if (!args.ParseOptional(0, range, r)) return CommandResult::ERROR; client.partition.Shuffle(range.start, range.end); @@ -223,37 +236,42 @@ handle_clear(gcc_unused Client &client, gcc_unused Request args) CommandResult handle_plchanges(Client &client, Request args) { - uint32_t version; + Response r(client); - if (!check_uint32(client, &version, args.front())) + uint32_t version; + if (!ParseCommandArg32(r, version, args.front())) return CommandResult::ERROR; - playlist_print_changes_info(client, client.playlist, version); + playlist_print_changes_info(r, client.partition, + client.playlist, version); return CommandResult::OK; } CommandResult handle_plchangesposid(Client &client, Request args) { - uint32_t version; + Response r(client); - if (!check_uint32(client, &version, args.front())) + uint32_t version; + if (!ParseCommandArg32(r, version, args.front())) return CommandResult::ERROR; - playlist_print_changes_position(client, client.playlist, version); + playlist_print_changes_position(r, client.playlist, version); return CommandResult::OK; } CommandResult handle_playlistinfo(Client &client, Request args) { + Response r(client); + RangeArg range = RangeArg::All(); - if (!args.ParseOptional(0, range, client)) + if (!args.ParseOptional(0, range, r)) return CommandResult::ERROR; - if (!playlist_print_info(client, client.playlist, + if (!playlist_print_info(r, client.partition, client.playlist, range.start, range.end)) - return print_playlist_result(client, + return print_playlist_result(r, PlaylistResult::BAD_RANGE); return CommandResult::OK; @@ -262,17 +280,19 @@ handle_playlistinfo(Client &client, Request args) CommandResult handle_playlistid(Client &client, Request args) { + Response r(client); + if (!args.IsEmpty()) { unsigned id; - if (!args.Parse(0, id, client)) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; - bool ret = playlist_print_id(client, client.playlist, id); + bool ret = playlist_print_id(r, client.partition, + client.playlist, id); if (!ret) - return print_playlist_result(client, - PlaylistResult::NO_SUCH_SONG); + return print_playlist_result(r, PlaylistResult::NO_SUCH_SONG); } else { - playlist_print_info(client, client.playlist, + playlist_print_info(r, client.partition, client.playlist, 0, std::numeric_limits::max()); } @@ -283,13 +303,15 @@ static CommandResult handle_playlist_match(Client &client, Request args, bool fold_case) { + Response r(client); + SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } - playlist_print_find(client, client.playlist, filter); + playlist_print_find(r, client.partition, client.playlist, filter); return CommandResult::OK; } @@ -308,13 +330,15 @@ handle_playlistsearch(Client &client, Request args) CommandResult handle_prio(Client &client, Request args) { + Response r(client); + unsigned priority; - if (!args.ParseShift(0, priority, client, 0xff)) + if (!args.ParseShift(0, priority, r, 0xff)) return CommandResult::ERROR; for (const char *i : args) { RangeArg range; - if (!ParseCommandArg(client, range, i)) + if (!ParseCommandArg(r, range, i)) return CommandResult::ERROR; PlaylistResult result = @@ -322,7 +346,7 @@ handle_prio(Client &client, Request args) range.end, priority); if (result != PlaylistResult::SUCCESS) - return print_playlist_result(client, result); + return print_playlist_result(r, result); } return CommandResult::OK; @@ -331,19 +355,21 @@ handle_prio(Client &client, Request args) CommandResult handle_prioid(Client &client, Request args) { + Response r(client); + unsigned priority; - if (!args.ParseShift(0, priority, client, 0xff)) + if (!args.ParseShift(0, priority, r, 0xff)) return CommandResult::ERROR; for (const char *i : args) { unsigned song_id; - if (!ParseCommandArg(client, song_id, i)) + if (!ParseCommandArg(r, song_id, i)) return CommandResult::ERROR; PlaylistResult result = client.partition.SetPriorityId(song_id, priority); if (result != PlaylistResult::SUCCESS) - return print_playlist_result(client, result); + return print_playlist_result(r, result); } return CommandResult::OK; @@ -352,52 +378,56 @@ handle_prioid(Client &client, Request args) CommandResult handle_move(Client &client, Request args) { + Response r(client); + RangeArg range; int to; - if (!args.Parse(0, range, client) || - !args.Parse(1, to, client)) + if (!args.Parse(0, range, r) || !args.Parse(1, to, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.MoveRange(range.start, range.end, to); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_moveid(Client &client, Request args) { + Response r(client); + unsigned id; int to; - if (!args.Parse(0, id, client) || - !args.Parse(1, to, client)) + if (!args.Parse(0, id, r) || !args.Parse(1, to, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.MoveId(id, to); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_swap(Client &client, Request args) { + Response r(client); + unsigned song1, song2; - if (!args.Parse(0, song1, client) || - !args.Parse(1, song2, client)) + if (!args.Parse(0, song1, r) || !args.Parse(1, song2, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SwapPositions(song1, song2); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult handle_swapid(Client &client, Request args) { + Response r(client); + unsigned id1, id2; - if (!args.Parse(0, id1, client) || - !args.Parse(1, id2, client)) + if (!args.Parse(0, id1, r) || !args.Parse(1, id2, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SwapIds(id1, id2); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } diff --git a/src/command/Request.hxx b/src/command/Request.hxx index 38b3c584c..1616b7045 100644 --- a/src/command/Request.hxx +++ b/src/command/Request.hxx @@ -28,7 +28,7 @@ #include -class Client; +class Response; class Request : public ConstBuffer { typedef ConstBuffer Base; @@ -45,26 +45,26 @@ public: } template - bool Parse(unsigned idx, T &value_r, Client &client, + bool Parse(unsigned idx, T &value_r, Response &r, Args&&... args) { assert(idx < size); - return ParseCommandArg(client, value_r, data[idx], + return ParseCommandArg(r, value_r, data[idx], std::forward(args)...); } template - bool ParseOptional(unsigned idx, T &value_r, Client &client, + bool ParseOptional(unsigned idx, T &value_r, Response &r, Args&&... args) { return idx >= size || - Parse(idx, value_r, client, + Parse(idx, value_r, r, std::forward(args)...); } template - bool ParseShift(unsigned idx, T &value_r, Client &client, + bool ParseShift(unsigned idx, T &value_r, Response &r, Args&&... args) { - bool success = Parse(idx, value_r, client, + bool success = Parse(idx, value_r, r, std::forward(args)...); shift(); return success; diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx index a28408547..25d354b2e 100644 --- a/src/command/StickerCommands.cxx +++ b/src/command/StickerCommands.cxx @@ -27,8 +27,8 @@ #include "sticker/StickerPrint.hxx" #include "sticker/StickerDatabase.hxx" #include "CommandError.hxx" -#include "protocol/Result.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "util/Error.hxx" @@ -37,7 +37,8 @@ #include struct sticker_song_find_data { - Client &client; + Response &r; + Partition &partition; const char *name; }; @@ -48,17 +49,17 @@ sticker_song_find_print_cb(const LightSong &song, const char *value, struct sticker_song_find_data *data = (struct sticker_song_find_data *)user_data; - song_print_uri(data->client, song); - sticker_print_value(data->client, data->name, value); + song_print_uri(data->r, data->partition, song); + sticker_print_value(data->r, data->name, value); } static CommandResult -handle_sticker_song(Client &client, Request args) +handle_sticker_song(Response &r, Partition &partition, Request args) { Error error; - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); const char *const cmd = args.front(); @@ -66,53 +67,52 @@ handle_sticker_song(Client &client, Request args) if (args.size == 4 && strcmp(cmd, "get") == 0) { const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); const auto value = sticker_song_get_value(*song, args[3], error); db->ReturnSong(song); if (value.empty()) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_NO_EXIST, - "no such sticker"); + r.Error(ACK_ERROR_NO_EXIST, "no such sticker"); return CommandResult::ERROR; } - sticker_print_value(client, args[3], value.c_str()); + sticker_print_value(r, args[3], value.c_str()); return CommandResult::OK; /* list song song_id */ } else if (args.size == 3 && strcmp(cmd, "list") == 0) { const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); Sticker *sticker = sticker_song_get(*song, error); db->ReturnSong(song); if (sticker) { - sticker_print(client, *sticker); + sticker_print(r, *sticker); sticker_free(sticker); } else if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; /* set song song_id id key */ } else if (args.size == 5 && strcmp(cmd, "set") == 0) { const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); bool ret = sticker_song_set_value(*song, args[3], args[4], error); db->ReturnSong(song); if (!ret) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_SYSTEM, - "failed to set sticker value"); + r.Error(ACK_ERROR_SYSTEM, + "failed to set sticker value"); return CommandResult::ERROR; } @@ -122,7 +122,7 @@ handle_sticker_song(Client &client, Request args) strcmp(cmd, "delete") == 0) { const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); bool ret = args.size == 3 ? sticker_song_delete(*song, error) @@ -130,10 +130,9 @@ handle_sticker_song(Client &client, Request args) db->ReturnSong(song); if (!ret) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_SYSTEM, - "no such sticker"); + r.Error(ACK_ERROR_SYSTEM, "no such sticker"); return CommandResult::ERROR; } @@ -161,14 +160,14 @@ handle_sticker_song(Client &client, Request args) else if (strcmp(op_s, ">") == 0) op = StickerOperator::GREATER_THAN; else { - command_error(client, ACK_ERROR_ARG, - "bad operator"); + r.Error(ACK_ERROR_ARG, "bad operator"); return CommandResult::ERROR; } } struct sticker_song_find_data data = { - client, + r, + partition, args[3], }; @@ -177,16 +176,16 @@ handle_sticker_song(Client &client, Request args) sticker_song_find_print_cb, &data, error)) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_SYSTEM, - "failed to set search sticker database"); + r.Error(ACK_ERROR_SYSTEM, + "failed to set search sticker database"); return CommandResult::ERROR; } return CommandResult::OK; } else { - command_error(client, ACK_ERROR_ARG, "bad request"); + r.Error(ACK_ERROR_ARG, "bad request"); return CommandResult::ERROR; } } @@ -194,19 +193,19 @@ handle_sticker_song(Client &client, Request args) CommandResult handle_sticker(Client &client, Request args) { + Response r(client); + assert(args.size >= 3); if (!sticker_enabled()) { - command_error(client, ACK_ERROR_UNKNOWN, - "sticker database is disabled"); + r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled"); return CommandResult::ERROR; } if (strcmp(args[1], "song") == 0) - return handle_sticker_song(client, args); + return handle_sticker_song(r, client.partition, args); else { - command_error(client, ACK_ERROR_ARG, - "unknown sticker domain"); + r.Error(ACK_ERROR_ARG, "unknown sticker domain"); return CommandResult::ERROR; } } diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx index 3f1e5f321..edf2f60b8 100644 --- a/src/command/StorageCommands.cxx +++ b/src/command/StorageCommands.cxx @@ -23,12 +23,12 @@ #include "StorageCommands.hxx" #include "Request.hxx" #include "CommandError.hxx" -#include "protocol/Result.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" #include "util/ConstBuffer.hxx" #include "fs/Traits.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "storage/Registry.hxx" @@ -57,7 +57,7 @@ skip_path(const char *name_utf8) #endif static bool -handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, +handle_listfiles_storage(Response &r, StorageDirectoryReader &reader, Error &error) { const char *name_utf8; @@ -75,19 +75,19 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, continue; case StorageFileInfo::Type::REGULAR: - client_printf(client, "file: %s\n" - "size: %" PRIu64 "\n", - name_utf8, - info.size); + r.Format("file: %s\n" + "size: %" PRIu64 "\n", + name_utf8, + info.size); break; case StorageFileInfo::Type::DIRECTORY: - client_printf(client, "directory: %s\n", name_utf8); + r.Format("directory: %s\n", name_utf8); break; } if (info.mtime != 0) - time_print(client, "Last-Modified", info.mtime); + time_print(r, "Last-Modified", info.mtime); } return true; @@ -98,52 +98,51 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, #endif static bool -handle_listfiles_storage(Client &client, Storage &storage, const char *uri, +handle_listfiles_storage(Response &r, Storage &storage, const char *uri, Error &error) { auto reader = storage.OpenDirectory(uri, error); if (reader == nullptr) return false; - bool success = handle_listfiles_storage(client, *reader, error); + bool success = handle_listfiles_storage(r, *reader, error); delete reader; return success; } CommandResult -handle_listfiles_storage(Client &client, Storage &storage, const char *uri) +handle_listfiles_storage(Response &r, Storage &storage, const char *uri) { Error error; - if (!handle_listfiles_storage(client, storage, uri, error)) - return print_error(client, error); + if (!handle_listfiles_storage(r, storage, uri, error)) + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_listfiles_storage(Client &client, const char *uri) +handle_listfiles_storage(Response &r, const char *uri) { Error error; Storage *storage = CreateStorageURI(io_thread_get(), uri, error); if (storage == nullptr) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_ARG, - "Unrecognized storage URI"); + r.Error(ACK_ERROR_ARG, "Unrecognized storage URI"); return CommandResult::ERROR; } - bool success = handle_listfiles_storage(client, *storage, "", error); + bool success = handle_listfiles_storage(r, *storage, "", error); delete storage; if (!success) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } static void -print_storage_uri(Client &client, const Storage &storage) +print_storage_uri(Client &client, Response &r, const Storage &storage) { std::string uri = storage.MapUTF8(""); if (uri.empty()) @@ -165,24 +164,26 @@ print_storage_uri(Client &client, const Storage &storage) uri = std::move(allocated); } - client_printf(client, "storage: %s\n", uri.c_str()); + r.Format("storage: %s\n", uri.c_str()); } CommandResult handle_listmounts(Client &client, gcc_unused Request args) { + Response r(client); + Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } CompositeStorage &composite = *(CompositeStorage *)_composite; - const auto visitor = [&client](const char *mount_uri, - const Storage &storage){ - client_printf(client, "mount: %s\n", mount_uri); - print_storage_uri(client, storage); + const auto visitor = [&client, &r](const char *mount_uri, + const Storage &storage){ + r.Format("mount: %s\n", mount_uri); + print_storage_uri(client, r, storage); }; composite.VisitMounts(visitor); @@ -193,9 +194,11 @@ handle_listmounts(Client &client, gcc_unused Request args) CommandResult handle_mount(Client &client, Request args) { + Response r(client); + Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } @@ -205,7 +208,7 @@ handle_mount(Client &client, Request args) const char *const remote_uri = args[1]; if (*local_uri == 0) { - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -215,7 +218,7 @@ handle_mount(Client &client, Request args) UpdateQueue::Erase() really gets called for every unmount, and no Directory disappears recursively during database update */ - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -224,10 +227,9 @@ handle_mount(Client &client, Request args) error); if (storage == nullptr) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_ARG, - "Unrecognized storage URI"); + r.Error(ACK_ERROR_ARG, "Unrecognized storage URI"); return CommandResult::ERROR; } @@ -241,7 +243,7 @@ handle_mount(Client &client, Request args) if (!db.Mount(local_uri, remote_uri, error)) { composite.Unmount(local_uri); - return print_error(client, error); + return print_error(r, error); } // TODO: call Instance::OnDatabaseModified()? @@ -256,9 +258,11 @@ handle_mount(Client &client, Request args) CommandResult handle_unmount(Client &client, Request args) { + Response r(client); + Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } @@ -267,7 +271,7 @@ handle_unmount(Client &client, Request args) const char *const local_uri = args.front(); if (*local_uri == 0) { - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -289,7 +293,7 @@ handle_unmount(Client &client, Request args) #endif if (!composite.Unmount(local_uri)) { - command_error(client, ACK_ERROR_ARG, "Not a mount point"); + r.Error(ACK_ERROR_ARG, "Not a mount point"); return CommandResult::ERROR; } diff --git a/src/command/StorageCommands.hxx b/src/command/StorageCommands.hxx index f5f962042..bbc0149bd 100644 --- a/src/command/StorageCommands.hxx +++ b/src/command/StorageCommands.hxx @@ -25,12 +25,13 @@ class Client; class Storage; class Request; +class Response; CommandResult -handle_listfiles_storage(Client &client, Storage &storage, const char *uri); +handle_listfiles_storage(Response &r, Storage &storage, const char *uri); CommandResult -handle_listfiles_storage(Client &client, const char *uri); +handle_listfiles_storage(Response &r, const char *uri); CommandResult handle_listmounts(Client &client, Request args); diff --git a/src/command/TagCommands.cxx b/src/command/TagCommands.cxx index 2690f39bf..35efa5859 100644 --- a/src/command/TagCommands.cxx +++ b/src/command/TagCommands.cxx @@ -22,7 +22,7 @@ #include "Request.hxx" #include "CommandError.hxx" #include "client/Client.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "tag/Tag.hxx" #include "Partition.hxx" #include "util/ConstBuffer.hxx" @@ -30,15 +30,16 @@ CommandResult handle_addtagid(Client &client, Request args) { + Response r(client); + unsigned song_id; - if (!args.Parse(0, song_id, client)) + if (!args.Parse(0, song_id, r)) return CommandResult::ERROR; const char *const tag_name = args[1]; const TagType tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, - "Unknown tag type: %s", tag_name); + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } @@ -47,7 +48,7 @@ handle_addtagid(Client &client, Request args) Error error; if (!client.partition.playlist.AddSongIdTag(song_id, tag_type, value, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } @@ -55,8 +56,10 @@ handle_addtagid(Client &client, Request args) CommandResult handle_cleartagid(Client &client, Request args) { + Response r(client); + unsigned song_id; - if (!args.Parse(0, song_id, client)) + if (!args.Parse(0, song_id, r)) return CommandResult::ERROR; TagType tag_type = TAG_NUM_OF_ITEM_TYPES; @@ -64,7 +67,7 @@ handle_cleartagid(Client &client, Request args) const char *const tag_name = args[1]; tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } @@ -73,7 +76,7 @@ handle_cleartagid(Client &client, Request args) Error error; if (!client.partition.playlist.ClearSongIdTag(song_id, tag_type, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } diff --git a/src/db/Count.cxx b/src/db/Count.cxx index 3e974deca..f97f2f288 100644 --- a/src/db/Count.cxx +++ b/src/db/Count.cxx @@ -21,7 +21,8 @@ #include "Count.hxx" #include "Selection.hxx" #include "Interface.hxx" -#include "client/Client.hxx" +#include "Partition.hxx" +#include "client/Response.hxx" #include "LightSong.hxx" #include "tag/Tag.hxx" @@ -40,26 +41,24 @@ class TagCountMap : public std::map { }; static void -PrintSearchStats(Client &client, const SearchStats &stats) +PrintSearchStats(Response &r, const SearchStats &stats) { unsigned total_duration_s = std::chrono::duration_cast(stats.total_duration).count(); - client_printf(client, - "songs: %u\n" - "playtime: %u\n", - stats.n_songs, total_duration_s); + r.Format("songs: %u\n" + "playtime: %u\n", + stats.n_songs, total_duration_s); } static void -Print(Client &client, TagType group, const TagCountMap &m) +Print(Response &r, TagType group, const TagCountMap &m) { assert(unsigned(group) < TAG_NUM_OF_ITEM_TYPES); for (const auto &i : m) { - client_printf(client, "%s: %s\n", - tag_item_names[group], i.first.c_str()); - PrintSearchStats(client, i.second); + r.Format("%s: %s\n", tag_item_names[group], i.first.c_str()); + PrintSearchStats(r, i.second); } } @@ -109,12 +108,12 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song) } bool -PrintSongCount(Client &client, const char *name, +PrintSongCount(Response &r, const Partition &partition, const char *name, const SongFilter *filter, TagType group, Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; @@ -131,7 +130,7 @@ PrintSongCount(Client &client, const char *name, if (!db->Visit(selection, f, error)) return false; - PrintSearchStats(client, stats); + PrintSearchStats(r, stats); } else { /* group by the specified tag: store counts in a std::map */ @@ -144,7 +143,7 @@ PrintSongCount(Client &client, const char *name, if (!db->Visit(selection, f, error)) return false; - Print(client, group, map); + Print(r, group, map); } return true; diff --git a/src/db/Count.hxx b/src/db/Count.hxx index d9f28cb03..4fc26aa7a 100644 --- a/src/db/Count.hxx +++ b/src/db/Count.hxx @@ -25,13 +25,14 @@ #include enum TagType : uint8_t; -class Client; +struct Partition; +class Response; class SongFilter; class Error; -gcc_nonnull(2) +gcc_nonnull(3) bool -PrintSongCount(Client &client, const char *name, +PrintSongCount(Response &r, const Partition &partition, const char *name, const SongFilter *filter, TagType group, Error &error); diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx index 33a4c65fe..3378df23b 100644 --- a/src/db/DatabasePrint.cxx +++ b/src/db/DatabasePrint.cxx @@ -24,6 +24,8 @@ #include "SongPrint.hxx" #include "TimePrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" +#include "Partition.hxx" #include "tag/Tag.hxx" #include "LightSong.hxx" #include "LightDirectory.hxx" @@ -42,116 +44,119 @@ ApplyBaseFlag(const char *uri, bool base) } static void -PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryURI(Response &r, bool base, const LightDirectory &directory) { - client_printf(client, "directory: %s\n", - ApplyBaseFlag(directory.GetPath(), base)); + r.Format("directory: %s\n", + ApplyBaseFlag(directory.GetPath(), base)); } static bool -PrintDirectoryBrief(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory) { if (!directory.IsRoot()) - PrintDirectoryURI(client, base, directory); + PrintDirectoryURI(r, base, directory); return true; } static bool -PrintDirectoryFull(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory) { if (!directory.IsRoot()) { - PrintDirectoryURI(client, base, directory); + PrintDirectoryURI(r, base, directory); if (directory.mtime > 0) - time_print(client, "Last-Modified", directory.mtime); + time_print(r, "Last-Modified", directory.mtime); } return true; } static void -print_playlist_in_directory(Client &client, bool base, +print_playlist_in_directory(Response &r, bool base, const char *directory, const char *name_utf8) { if (base || directory == nullptr) - client_printf(client, "playlist: %s\n", - ApplyBaseFlag(name_utf8, base)); + r.Format("playlist: %s\n", + ApplyBaseFlag(name_utf8, base)); else - client_printf(client, "playlist: %s/%s\n", - directory, name_utf8); + r.Format("playlist: %s/%s\n", + directory, name_utf8); } static void -print_playlist_in_directory(Client &client, bool base, +print_playlist_in_directory(Response &r, bool base, const LightDirectory *directory, const char *name_utf8) { if (base || directory == nullptr || directory->IsRoot()) - client_printf(client, "playlist: %s\n", name_utf8); + r.Format("playlist: %s\n", name_utf8); else - client_printf(client, "playlist: %s/%s\n", - directory->GetPath(), name_utf8); + r.Format("playlist: %s/%s\n", + directory->GetPath(), name_utf8); } static bool -PrintSongBrief(Client &client, bool base, const LightSong &song) +PrintSongBrief(Response &r, Partition &partition, + bool base, const LightSong &song) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); if (song.tag->has_playlist) /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, song.directory, song.uri); return true; } static bool -PrintSongFull(Client &client, bool base, const LightSong &song) +PrintSongFull(Response &r, Partition &partition, + bool base, const LightSong &song) { - song_print_info(client, song, base); + song_print_info(r, partition, song, base); if (song.tag->has_playlist) /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, song.directory, song.uri); return true; } static bool -PrintPlaylistBrief(Client &client, bool base, +PrintPlaylistBrief(Response &r, bool base, const PlaylistInfo &playlist, const LightDirectory &directory) { - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, &directory, playlist.name.c_str()); return true; } static bool -PrintPlaylistFull(Client &client, bool base, +PrintPlaylistFull(Response &r, bool base, const PlaylistInfo &playlist, const LightDirectory &directory) { - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, &directory, playlist.name.c_str()); if (playlist.mtime > 0) - time_print(client, "Last-Modified", playlist.mtime); + time_print(r, "Last-Modified", playlist.mtime); return true; } bool -db_selection_print(Client &client, const DatabaseSelection &selection, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, bool full, bool base, unsigned window_start, unsigned window_end, Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; @@ -160,13 +165,13 @@ db_selection_print(Client &client, const DatabaseSelection &selection, using namespace std::placeholders; const auto d = selection.filter == nullptr ? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief, - std::ref(client), base, _1) + std::ref(r), base, _1) : VisitDirectory(); VisitSong s = std::bind(full ? PrintSongFull : PrintSongBrief, - std::ref(client), base, _1); + std::ref(r), std::ref(partition), base, _1); const auto p = selection.filter == nullptr ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief, - std::ref(client), base, _1, _2) + std::ref(r), base, _1, _2) : VisitPlaylist(); if (window_start > 0 || @@ -182,45 +187,47 @@ db_selection_print(Client &client, const DatabaseSelection &selection, } bool -db_selection_print(Client &client, const DatabaseSelection &selection, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, bool full, bool base, Error &error) { - return db_selection_print(client, selection, full, base, + return db_selection_print(r, partition, selection, full, base, 0, std::numeric_limits::max(), error); } static bool -PrintSongURIVisitor(Client &client, const LightSong &song) +PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song) { - song_print_uri(client, song); + song_print_uri(r, partition, song); return true; } static bool -PrintUniqueTag(Client &client, TagType tag_type, +PrintUniqueTag(Response &r, TagType tag_type, const Tag &tag) { const char *value = tag.GetValue(tag_type); assert(value != nullptr); - client_printf(client, "%s: %s\n", tag_item_names[tag_type], value); + r.Format("%s: %s\n", tag_item_names[tag_type], value); for (const auto &item : tag) if (item.type != tag_type) - client_printf(client, "%s: %s\n", - tag_item_names[item.type], item.value); + r.Format("%s: %s\n", + tag_item_names[item.type], item.value); return true; } bool -PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, +PrintUniqueTags(Response &r, Partition &partition, + unsigned type, uint32_t group_mask, const SongFilter *filter, Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; @@ -229,13 +236,13 @@ PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, if (type == LOCATE_TAG_FILE_TYPE) { using namespace std::placeholders; const auto f = std::bind(PrintSongURIVisitor, - std::ref(client), _1); + std::ref(r), std::ref(partition), _1); return db->Visit(selection, f, error); } else { assert(type < TAG_NUM_OF_ITEM_TYPES); using namespace std::placeholders; - const auto f = std::bind(PrintUniqueTag, std::ref(client), + const auto f = std::bind(PrintUniqueTag, std::ref(r), (TagType)type, _1); return db->VisitUniqueTags(selection, (TagType)type, group_mask, diff --git a/src/db/DatabasePrint.hxx b/src/db/DatabasePrint.hxx index 1c228a507..cf98d31df 100644 --- a/src/db/DatabasePrint.hxx +++ b/src/db/DatabasePrint.hxx @@ -26,7 +26,9 @@ class SongFilter; struct DatabaseSelection; +struct Partition; class Client; +class Response; class Error; /** @@ -34,17 +36,20 @@ class Error; * @param base print only base name of songs/directories? */ bool -db_selection_print(Client &client, const DatabaseSelection &selection, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, bool full, bool base, Error &error); bool -db_selection_print(Client &client, const DatabaseSelection &selection, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, bool full, bool base, unsigned window_start, unsigned window_end, Error &error); bool -PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, +PrintUniqueTags(Response &r, Partition &partition, + unsigned type, uint32_t group_mask, const SongFilter *filter, Error &error); diff --git a/src/decoder/DecoderPrint.cxx b/src/decoder/DecoderPrint.cxx index fe7206410..6a0822596 100644 --- a/src/decoder/DecoderPrint.cxx +++ b/src/decoder/DecoderPrint.cxx @@ -21,35 +21,35 @@ #include "DecoderPrint.hxx" #include "DecoderList.hxx" #include "DecoderPlugin.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" #include #include static void -decoder_plugin_print(Client &client, +decoder_plugin_print(Response &r, const DecoderPlugin &plugin) { const char *const*p; assert(plugin.name != nullptr); - client_printf(client, "plugin: %s\n", plugin.name); + r.Format("plugin: %s\n", plugin.name); if (plugin.suffixes != nullptr) for (p = plugin.suffixes; *p != nullptr; ++p) - client_printf(client, "suffix: %s\n", *p); + r.Format("suffix: %s\n", *p); if (plugin.mime_types != nullptr) for (p = plugin.mime_types; *p != nullptr; ++p) - client_printf(client, "mime_type: %s\n", *p); + r.Format("mime_type: %s\n", *p); } void -decoder_list_print(Client &client) +decoder_list_print(Response &r) { using namespace std::placeholders; - const auto f = std::bind(decoder_plugin_print, std::ref(client), _1); + const auto f = std::bind(decoder_plugin_print, std::ref(r), _1); decoder_plugins_for_each_enabled(f); } diff --git a/src/decoder/DecoderPrint.hxx b/src/decoder/DecoderPrint.hxx index f8dd03c08..fcb995f6b 100644 --- a/src/decoder/DecoderPrint.hxx +++ b/src/decoder/DecoderPrint.hxx @@ -20,9 +20,9 @@ #ifndef MPD_DECODER_PRINT_HXX #define MPD_DECODER_PRINT_HXX -class Client; +class Response; void -decoder_list_print(Client &client); +decoder_list_print(Response &r); #endif diff --git a/src/ls.cxx b/src/ls.cxx index 0287f5137..41bd4ba97 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -19,9 +19,9 @@ #include "config.h" #include "ls.hxx" +#include "client/Response.hxx" #include "util/StringUtil.hxx" #include "util/UriUtil.hxx" -#include "client/Client.hxx" #include @@ -78,12 +78,13 @@ void print_supported_uri_schemes_to_fp(FILE *fp) fprintf(fp,"\n"); } -void print_supported_uri_schemes(Client &client) +void +print_supported_uri_schemes(Response &r) { const char *const *prefixes = remoteUrlPrefixes; while (*prefixes) { - client_printf(client, "handler: %s\n", *prefixes); + r.Format("handler: %s\n", *prefixes); prefixes++; } } diff --git a/src/ls.hxx b/src/ls.hxx index 27843a657..f34e3c6ab 100644 --- a/src/ls.hxx +++ b/src/ls.hxx @@ -24,7 +24,7 @@ #include -class Client; +class Response; /** * Checks whether the scheme of the specified URI is supported by MPD. @@ -38,7 +38,7 @@ bool uri_supported_scheme(const char *url); * Send a list of supported URI schemes to the client. This is the * response to the "urlhandlers" command. */ -void print_supported_uri_schemes(Client &client); +void print_supported_uri_schemes(Response &r); /** * Send a list of supported URI schemes to a file pointer. diff --git a/src/output/OutputPrint.cxx b/src/output/OutputPrint.cxx index 831aea649..d2ddbbf8b 100644 --- a/src/output/OutputPrint.cxx +++ b/src/output/OutputPrint.cxx @@ -26,18 +26,17 @@ #include "OutputPrint.hxx" #include "MultipleOutputs.hxx" #include "Internal.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -printAudioDevices(Client &client, const MultipleOutputs &outputs) +printAudioDevices(Response &r, const MultipleOutputs &outputs) { for (unsigned i = 0, n = outputs.Size(); i != n; ++i) { const AudioOutput &ao = outputs.Get(i); - client_printf(client, - "outputid: %i\n" - "outputname: %s\n" - "outputenabled: %i\n", - i, ao.name, ao.enabled); + r.Format("outputid: %i\n" + "outputname: %s\n" + "outputenabled: %i\n", + i, ao.name, ao.enabled); } } diff --git a/src/output/OutputPrint.hxx b/src/output/OutputPrint.hxx index 2679f6a70..e05c8efd5 100644 --- a/src/output/OutputPrint.hxx +++ b/src/output/OutputPrint.hxx @@ -25,10 +25,10 @@ #ifndef MPD_OUTPUT_PRINT_HXX #define MPD_OUTPUT_PRINT_HXX -class Client; +class Response; class MultipleOutputs; void -printAudioDevices(Client &client, const MultipleOutputs &outputs); +printAudioDevices(Response &r, const MultipleOutputs &outputs); #endif diff --git a/src/playlist/Print.cxx b/src/playlist/Print.cxx index 3de9807b7..13e45d160 100644 --- a/src/playlist/Print.cxx +++ b/src/playlist/Print.cxx @@ -28,48 +28,51 @@ #include "fs/Traits.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" -#include "client/Client.hxx" +#include "Partition.hxx" +#include "Instance.hxx" static void -playlist_provider_print(Client &client, const char *uri, +playlist_provider_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, SongEnumerator &e, bool detail) { const std::string base_uri = uri != nullptr ? PathTraitsUTF8::GetParent(uri) : std::string("."); - const SongLoader loader(client); - DetachedSong *song; while ((song = e.NextSong()) != nullptr) { if (playlist_check_translate_song(*song, base_uri.c_str(), loader) && detail) - song_print_info(client, *song); + song_print_info(r, partition, *song); else /* fallback if no detail was requested or no detail was available */ - song_print_uri(client, *song); + song_print_uri(r, partition, *song); delete song; } } bool -playlist_file_print(Client &client, const char *uri, bool detail) +playlist_file_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, bool detail) { Mutex mutex; Cond cond; SongEnumerator *playlist = playlist_open_any(uri, #ifdef ENABLE_DATABASE - client.GetStorage(), + partition.instance.storage, #endif mutex, cond); if (playlist == nullptr) return false; - playlist_provider_print(client, uri, *playlist, detail); + playlist_provider_print(r, partition, loader, uri, *playlist, detail); delete playlist; return true; } diff --git a/src/playlist/Print.hxx b/src/playlist/Print.hxx index 02096bdc2..3b356d4ce 100644 --- a/src/playlist/Print.hxx +++ b/src/playlist/Print.hxx @@ -20,17 +20,20 @@ #ifndef MPD_PLAYLIST__PRINT_HXX #define MPD_PLAYLIST__PRINT_HXX -class Client; +class Response; +class SongLoader; +struct Partition; /** * Send the playlist file to the client. * - * @param client the client which requested the playlist * @param uri the URI of the playlist file in UTF-8 encoding * @param detail true if all details should be printed * @return true on success, false if the playlist does not exist */ bool -playlist_file_print(Client &client, const char *uri, bool detail); +playlist_file_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, bool detail); #endif diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx index 580d70a20..31756f53e 100644 --- a/src/protocol/ArgParser.cxx +++ b/src/protocol/ArgParser.cxx @@ -19,27 +19,26 @@ #include "config.h" #include "ArgParser.hxx" -#include "Result.hxx" #include "Chrono.hxx" +#include "client/Response.hxx" #include bool -check_uint32(Client &client, uint32_t *dst, const char *s) +ParseCommandArg32(Response &r, uint32_t &value_r, const char *s) { char *test; - *dst = strtoul(s, &test, 10); + value_r = strtoul(s, &test, 10); if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } return true; } bool -ParseCommandArg(Client &client, int &value_r, const char *s, +ParseCommandArg(Response &r, int &value_r, const char *s, int min_value, int max_value) { char *test; @@ -47,14 +46,12 @@ ParseCommandArg(Client &client, int &value_r, const char *s, value = strtol(s, &test, 10); if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } if (value < min_value || value > max_value) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } @@ -63,22 +60,22 @@ ParseCommandArg(Client &client, int &value_r, const char *s, } bool -ParseCommandArg(Client &client, int &value_r, const char *s) +ParseCommandArg(Response &r, int &value_r, const char *s) { - return ParseCommandArg(client, value_r, s, + return ParseCommandArg(r, value_r, s, std::numeric_limits::min(), std::numeric_limits::max()); } bool -ParseCommandArg(Client &client, RangeArg &value_r, const char *s) +ParseCommandArg(Response &r, RangeArg &value_r, const char *s) { char *test, *test2; long value; value = strtol(s, &test, 10); if (test == s || (*test != '\0' && *test != ':')) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Integer or range expected: %s", s); return false; } @@ -92,14 +89,12 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s) } if (value < 0) { - command_error(client, ACK_ERROR_ARG, - "Number is negative: %s", s); + r.FormatError(ACK_ERROR_ARG, "Number is negative: %s", s); return false; } if (unsigned(value) > std::numeric_limits::max()) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } @@ -108,7 +103,7 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s) if (*test == ':') { value = strtol(++test, &test2, 10); if (*test2 != '\0') { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Integer or range expected: %s", s); return false; } @@ -117,13 +112,13 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s) value = std::numeric_limits::max(); if (value < 0) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Number is negative: %s", s); return false; } if (unsigned(value) > std::numeric_limits::max()) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } @@ -137,7 +132,7 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s) } bool -ParseCommandArg(Client &client, unsigned &value_r, const char *s, +ParseCommandArg(Response &r, unsigned &value_r, const char *s, unsigned max_value) { unsigned long value; @@ -145,13 +140,12 @@ ParseCommandArg(Client &client, unsigned &value_r, const char *s, value = strtoul(s, &endptr, 10); if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } if (value > max_value) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } @@ -161,21 +155,21 @@ ParseCommandArg(Client &client, unsigned &value_r, const char *s, } bool -ParseCommandArg(Client &client, unsigned &value_r, const char *s) +ParseCommandArg(Response &r, unsigned &value_r, const char *s) { - return ParseCommandArg(client, value_r, s, + return ParseCommandArg(r, value_r, s, std::numeric_limits::max()); } bool -ParseCommandArg(Client &client, bool &value_r, const char *s) +ParseCommandArg(Response &r, bool &value_r, const char *s) { long value; char *endptr; value = strtol(s, &endptr, 10); if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Boolean (0/1) expected: %s", s); return false; } @@ -185,15 +179,14 @@ ParseCommandArg(Client &client, bool &value_r, const char *s) } bool -ParseCommandArg(Client &client, float &value_r, const char *s) +ParseCommandArg(Response &r, float &value_r, const char *s) { float value; char *endptr; value = strtof(s, &endptr); if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Float expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Float expected: %s", s); return false; } @@ -202,10 +195,10 @@ ParseCommandArg(Client &client, float &value_r, const char *s) } bool -ParseCommandArg(Client &client, SongTime &value_r, const char *s) +ParseCommandArg(Response &r, SongTime &value_r, const char *s) { float value; - bool success = ParseCommandArg(client, value, s) && value >= 0; + bool success = ParseCommandArg(r, value, s) && value >= 0; if (success) value_r = SongTime::FromS(value); @@ -213,10 +206,10 @@ ParseCommandArg(Client &client, SongTime &value_r, const char *s) } bool -ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s) +ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s) { float value; - bool success = ParseCommandArg(client, value, s); + bool success = ParseCommandArg(r, value, s); if (success) value_r = SignedSongTime::FromS(value); diff --git a/src/protocol/ArgParser.hxx b/src/protocol/ArgParser.hxx index b05d7224e..f60dbdf50 100644 --- a/src/protocol/ArgParser.hxx +++ b/src/protocol/ArgParser.hxx @@ -26,19 +26,19 @@ #include -class Client; +class Response; class SongTime; class SignedSongTime; bool -check_uint32(Client &client, uint32_t *dst, const char *s); +ParseCommandArg32(Response &r, uint32_t &value_r, const char *s); bool -ParseCommandArg(Client &client, int &value_r, const char *s, +ParseCommandArg(Response &r, int &value_r, const char *s, int min_value, int max_value); bool -ParseCommandArg(Client &client, int &value_r, const char *s); +ParseCommandArg(Response &r, int &value_r, const char *s); struct RangeArg { unsigned start, end; @@ -54,25 +54,25 @@ struct RangeArg { }; bool -ParseCommandArg(Client &client, RangeArg &value_r, const char *s); +ParseCommandArg(Response &r, RangeArg &value_r, const char *s); bool -ParseCommandArg(Client &client, unsigned &value_r, const char *s, +ParseCommandArg(Response &r, unsigned &value_r, const char *s, unsigned max_value); bool -ParseCommandArg(Client &client, unsigned &value_r, const char *s); +ParseCommandArg(Response &r, unsigned &value_r, const char *s); bool -ParseCommandArg(Client &client, bool &value_r, const char *s); +ParseCommandArg(Response &r, bool &value_r, const char *s); bool -ParseCommandArg(Client &client, float &value_r, const char *s); +ParseCommandArg(Response &r, float &value_r, const char *s); bool -ParseCommandArg(Client &client, SongTime &value_r, const char *s); +ParseCommandArg(Response &r, SongTime &value_r, const char *s); bool -ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s); +ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s); #endif diff --git a/src/queue/QueuePrint.cxx b/src/queue/QueuePrint.cxx index c5bda5607..5ae1a3036 100644 --- a/src/queue/QueuePrint.cxx +++ b/src/queue/QueuePrint.cxx @@ -22,7 +22,7 @@ #include "Queue.hxx" #include "SongFilter.hxx" #include "SongPrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" /** * Send detailed information about a range of songs in the queue to a @@ -33,70 +33,70 @@ * @param end the index of the last song (excluding) */ static void -queue_print_song_info(Client &client, const Queue &queue, +queue_print_song_info(Response &r, Partition &partition, const Queue &queue, unsigned position) { - song_print_info(client, queue.Get(position)); - client_printf(client, "Pos: %u\nId: %u\n", - position, queue.PositionToId(position)); + song_print_info(r, partition, queue.Get(position)); + r.Format("Pos: %u\nId: %u\n", + position, queue.PositionToId(position)); uint8_t priority = queue.GetPriorityAtPosition(position); if (priority != 0) - client_printf(client, "Prio: %u\n", priority); + r.Format("Prio: %u\n", priority); } void -queue_print_info(Client &client, const Queue &queue, +queue_print_info(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end) { assert(start <= end); assert(end <= queue.GetLength()); for (unsigned i = start; i < end; ++i) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } void -queue_print_uris(Client &client, const Queue &queue, +queue_print_uris(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end) { assert(start <= end); assert(end <= queue.GetLength()); for (unsigned i = start; i < end; ++i) { - client_printf(client, "%i:", i); - song_print_uri(client, queue.Get(i)); + r.Format("%i:", i); + song_print_uri(r, partition, queue.Get(i)); } } void -queue_print_changes_info(Client &client, const Queue &queue, +queue_print_changes_info(Response &r, Partition &partition, const Queue &queue, uint32_t version) { for (unsigned i = 0; i < queue.GetLength(); i++) { if (queue.IsNewerAtPosition(i, version)) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } } void -queue_print_changes_position(Client &client, const Queue &queue, +queue_print_changes_position(Response &r, const Queue &queue, uint32_t version) { for (unsigned i = 0; i < queue.GetLength(); i++) if (queue.IsNewerAtPosition(i, version)) - client_printf(client, "cpos: %i\nId: %i\n", - i, queue.PositionToId(i)); + r.Format("cpos: %i\nId: %i\n", + i, queue.PositionToId(i)); } void -queue_find(Client &client, const Queue &queue, +queue_find(Response &r, Partition &partition, const Queue &queue, const SongFilter &filter) { for (unsigned i = 0; i < queue.GetLength(); i++) { const DetachedSong &song = queue.Get(i); if (filter.Match(song)) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } } diff --git a/src/queue/QueuePrint.hxx b/src/queue/QueuePrint.hxx index 6589ee93e..88d28e8ca 100644 --- a/src/queue/QueuePrint.hxx +++ b/src/queue/QueuePrint.hxx @@ -28,27 +28,28 @@ #include struct Queue; +struct Partition; class SongFilter; -class Client; +class Response; void -queue_print_info(Client &client, const Queue &queue, +queue_print_info(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end); void -queue_print_uris(Client &client, const Queue &queue, +queue_print_uris(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end); void -queue_print_changes_info(Client &client, const Queue &queue, +queue_print_changes_info(Response &r, Partition &partition, const Queue &queue, uint32_t version); void -queue_print_changes_position(Client &client, const Queue &queue, +queue_print_changes_position(Response &r, const Queue &queue, uint32_t version); void -queue_find(Client &client, const Queue &queue, +queue_find(Response &response, Partition &partition, const Queue &queue, const SongFilter &filter); #endif diff --git a/src/sticker/StickerPrint.cxx b/src/sticker/StickerPrint.cxx index 1682abf77..f0043ebc8 100644 --- a/src/sticker/StickerPrint.cxx +++ b/src/sticker/StickerPrint.cxx @@ -20,25 +20,25 @@ #include "config.h" #include "StickerPrint.hxx" #include "StickerDatabase.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -sticker_print_value(Client &client, +sticker_print_value(Response &r, const char *name, const char *value) { - client_printf(client, "sticker: %s=%s\n", name, value); + r.Format("sticker: %s=%s\n", name, value); } static void print_sticker_cb(const char *name, const char *value, void *data) { - Client &client = *(Client *)data; + auto &r = *(Response *)data; - sticker_print_value(client, name, value); + sticker_print_value(r, name, value); } void -sticker_print(Client &client, const Sticker &sticker) +sticker_print(Response &r, const Sticker &sticker) { - sticker_foreach(sticker, print_sticker_cb, &client); + sticker_foreach(sticker, print_sticker_cb, &r); } diff --git a/src/sticker/StickerPrint.hxx b/src/sticker/StickerPrint.hxx index 53aaca8df..e431245e4 100644 --- a/src/sticker/StickerPrint.hxx +++ b/src/sticker/StickerPrint.hxx @@ -21,18 +21,18 @@ #define MPD_STICKER_PRINT_HXX struct Sticker; -class Client; +class Response; /** * Sends one sticker value to the client. */ void -sticker_print_value(Client &client, const char *name, const char *value); +sticker_print_value(Response &r, const char *name, const char *value); /** * Sends all sticker values to the client. */ void -sticker_print(Client &client, const Sticker &sticker); +sticker_print(Response &r, const Sticker &sticker); #endif diff --git a/test/test_protocol.cxx b/test/test_protocol.cxx index d5b60323a..457083aee 100644 --- a/test/test_protocol.cxx +++ b/test/test_protocol.cxx @@ -1,6 +1,6 @@ #include "config.h" #include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "Compiler.h" #include @@ -13,10 +13,15 @@ static enum ack last_error = ack(-1); void -command_error(gcc_unused Client &client, enum ack error, - gcc_unused const char *fmt, ...) +Response::Error(enum ack code, gcc_unused const char *msg) { - last_error = error; + last_error = code; +} + +void +Response::FormatError(enum ack code, gcc_unused const char *fmt, ...) +{ + last_error = code; } class ArgParserTest : public CppUnit::TestFixture { @@ -32,22 +37,23 @@ void ArgParserTest::TestRange() { Client &client = *(Client *)nullptr; + Response r(client); RangeArg range; - CPPUNIT_ASSERT(ParseCommandArg(client, range, "1")); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1")); CPPUNIT_ASSERT_EQUAL(1u, range.start); CPPUNIT_ASSERT_EQUAL(2u, range.end); - CPPUNIT_ASSERT(ParseCommandArg(client, range, "1:5")); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:5")); CPPUNIT_ASSERT_EQUAL(1u, range.start); CPPUNIT_ASSERT_EQUAL(5u, range.end); - CPPUNIT_ASSERT(ParseCommandArg(client, range, "1:")); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:")); CPPUNIT_ASSERT_EQUAL(1u, range.start); CPPUNIT_ASSERT(range.end >= 999999u); - CPPUNIT_ASSERT(!ParseCommandArg(client, range, "-2")); + CPPUNIT_ASSERT(!ParseCommandArg(r, range, "-2")); CPPUNIT_ASSERT_EQUAL(ACK_ERROR_ARG, last_error); }