diff options
author | Max Kellermann <max@duempel.org> | 2015-10-22 09:29:02 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2015-10-22 09:39:28 +0200 |
commit | f072cbbba765e3b972655880970d65760a208843 (patch) | |
tree | b2065cf07d599f7f44c39bb3c3537f8f72691a73 /src/command | |
parent | 1a5b66b78dc767a4e0b721a2325957fa9ae815c2 (diff) | |
download | mpd-f072cbbba765e3b972655880970d65760a208843.tar.gz mpd-f072cbbba765e3b972655880970d65760a208843.tar.xz mpd-f072cbbba765e3b972655880970d65760a208843.zip |
LocateUri: new library to classify URIs in a standard way
Diffstat (limited to 'src/command')
-rw-r--r-- | src/command/CommandError.cxx | 4 | ||||
-rw-r--r-- | src/command/FileCommands.cxx | 109 | ||||
-rw-r--r-- | src/command/FileCommands.hxx | 5 | ||||
-rw-r--r-- | src/command/OtherCommands.cxx | 156 | ||||
-rw-r--r-- | src/command/QueueCommands.cxx | 82 |
5 files changed, 205 insertions, 151 deletions
diff --git a/src/command/CommandError.cxx b/src/command/CommandError.cxx index d95722c3b..9f06431b4 100644 --- a/src/command/CommandError.cxx +++ b/src/command/CommandError.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "CommandError.hxx" #include "db/DatabaseError.hxx" +#include "LocateUri.hxx" #include "client/Response.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -114,6 +115,9 @@ print_error(Response &r, const Error &error) return CommandResult::ERROR; } #endif + } else if (error.IsDomain(locate_uri_domain)) { + r.Error(ACK_ERROR_ARG, error.GetMessage()); + return CommandResult::ERROR; } else if (error.IsDomain(errno_domain)) { r.Error(ACK_ERROR_SYSTEM, strerror(error.GetCode())); return CommandResult::ERROR; diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index f7e16a0d2..486c00d89 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -39,8 +39,8 @@ #include "fs/AllocatedPath.hxx" #include "fs/FileInfo.hxx" #include "fs/DirectoryReader.hxx" +#include "LocateUri.hxx" #include "TimePrint.hxx" -#include "ls.hxx" #include <assert.h> #include <sys/stat.h> @@ -70,21 +70,12 @@ skip_path(Path name_fs) #endif CommandResult -handle_listfiles_local(Client &client, Response &r, - const char *path_utf8) +handle_listfiles_local(Response &r, + const char *path_utf8, Path path_fs) { - const auto path_fs = AllocatedPath::FromUTF8(path_utf8); - if (path_fs.IsNull()) { - r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(r, error); - DirectoryReader reader(path_fs); if (reader.HasFailed()) { + Error error; error.FormatErrno("Failed to open '%s'", path_utf8); return print_error(r, error); } @@ -172,11 +163,6 @@ static constexpr tag_handler print_comment_handler = { static CommandResult read_stream_comments(Response &r, const char *uri) { - if (!uri_supported_scheme(uri)) { - r.Error(ACK_ERROR_NO_EXIST, "unsupported URI scheme"); - return CommandResult::ERROR; - } - if (!tag_stream_scan(uri, print_comment_handler, &r)) { r.Error(ACK_ERROR_NO_EXIST, "Failed to load file"); return CommandResult::ERROR; @@ -201,63 +187,64 @@ read_file_comments(Response &r, const Path path_fs) } -static const char * -translate_uri(const char *uri) +static CommandResult +read_db_comments(Client &client, Response &r, const char *uri) { - if (memcmp(uri, "file:///", 8) == 0) - /* drop the "file://", leave only an absolute path - (starting with a slash) */ - return uri + 7; +#ifdef ENABLE_DATABASE + const Storage *storage = client.GetStorage(); + if (storage == nullptr) { +#else + (void)client; + (void)uri; +#endif + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#ifdef ENABLE_DATABASE + } + + { + AllocatedPath path_fs = storage->MapFS(uri); + if (!path_fs.IsNull()) + return read_file_comments(r, path_fs); + } - return uri; + { + const std::string uri2 = storage->MapUTF8(uri); + if (uri_has_scheme(uri2.c_str())) + return read_stream_comments(r, uri2.c_str()); + } + + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; +#endif } CommandResult handle_read_comments(Client &client, Request args, Response &r) { assert(args.size == 1); - const char *const uri = translate_uri(args.front()); - - if (PathTraitsUTF8::IsAbsolute(uri)) { - /* read comments from arbitrary local file */ - const char *path_utf8 = uri; - AllocatedPath path_fs = AllocatedPath::FromUTF8(path_utf8); - if (path_fs.IsNull()) { - r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); - return CommandResult::ERROR; - } - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(r, error); + const char *const uri = args.front(); - return read_file_comments(r, path_fs); - } else if (uri_has_scheme(uri)) { - return read_stream_comments(r, uri); - } else { + Error error; + const auto located_uri = LocateUri(uri, &client, #ifdef ENABLE_DATABASE - const Storage *storage = client.GetStorage(); - if (storage == nullptr) { + nullptr, #endif - r.Error(ACK_ERROR_NO_EXIST, "No database"); - return CommandResult::ERROR; -#ifdef ENABLE_DATABASE - } + error); + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); - { - AllocatedPath path_fs = storage->MapFS(uri); - if (!path_fs.IsNull()) - return read_file_comments(r, path_fs); - } + case LocatedUri::Type::ABSOLUTE: + return read_stream_comments(r, located_uri.canonical_uri); - { - const std::string uri2 = storage->MapUTF8(uri); - if (uri_has_scheme(uri2.c_str())) - return read_stream_comments(r, uri2.c_str()); - } + case LocatedUri::Type::RELATIVE: + return read_db_comments(client, r, located_uri.canonical_uri); - r.Error(ACK_ERROR_NO_EXIST, "No such file"); - return CommandResult::ERROR; -#endif + case LocatedUri::Type::PATH: + return read_file_comments(r, located_uri.path); } + + gcc_unreachable(); } diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx index c072d2028..9c6631df5 100644 --- a/src/command/FileCommands.hxx +++ b/src/command/FileCommands.hxx @@ -25,10 +25,11 @@ class Client; class Request; class Response; +class Path; CommandResult -handle_listfiles_local(Client &client, Response &response, - const char *path_utf8); +handle_listfiles_local(Response &response, + const char *path_utf8, Path path_fs); CommandResult handle_read_comments(Client &client, Request request, Response &response); diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index 09b951b03..b4a23fe4b 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -25,6 +25,7 @@ #include "CommandError.hxx" #include "db/Uri.hxx" #include "storage/StorageInterface.hxx" +#include "LocateUri.hxx" #include "DetachedSong.hxx" #include "SongPrint.hxx" #include "TagPrint.hxx" @@ -122,31 +123,49 @@ handle_listfiles(Client &client, Request args, Response &r) /* default is root directory */ const auto uri = args.GetOptional(0, ""); - if (memcmp(uri, "file:///", 8) == 0) - /* list local directory */ - return handle_listfiles_local(client, r, uri + 7); - + Error error; + const auto located_uri = LocateUri(uri, &client, #ifdef ENABLE_DATABASE - if (uri_has_scheme(uri)) - /* use storage plugin to list remote directory */ - return handle_listfiles_storage(r, uri); + nullptr, +#endif + error); - /* must be a path relative to the configured - music_directory */ + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); - if (client.partition.instance.storage != nullptr) - /* if we have a storage instance, obtain a list of - files from it */ - return handle_listfiles_storage(r, - *client.partition.instance.storage, - uri); + case LocatedUri::Type::ABSOLUTE: +#ifdef ENABLE_DATABASE + /* use storage plugin to list remote directory */ + return handle_listfiles_storage(r, located_uri.canonical_uri); +#else + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#endif - /* fall back to entries from database if we have no storage */ - return handle_listfiles_db(client, r, uri); + case LocatedUri::Type::RELATIVE: +#ifdef ENABLE_DATABASE + if (client.partition.instance.storage != nullptr) + /* if we have a storage instance, obtain a list of + files from it */ + return handle_listfiles_storage(r, + *client.partition.instance.storage, + uri); + + /* fall back to entries from database if we have no storage */ + return handle_listfiles_db(client, r, uri); #else - r.Error(ACK_ERROR_NO_EXIST, "No database"); - return CommandResult::ERROR; + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; #endif + + case LocatedUri::Type::PATH: + /* list local directory */ + return handle_listfiles_local(r, located_uri.canonical_uri, + located_uri.path); + } + + gcc_unreachable(); } static constexpr tag_handler print_tag_handler = { @@ -155,54 +174,26 @@ static constexpr tag_handler print_tag_handler = { nullptr, }; -CommandResult -handle_lsinfo(Client &client, Request args, Response &r) +static CommandResult +handle_lsinfo_absolute(Response &r, const char *uri) { - /* default is root directory */ - const auto uri = args.GetOptional(0, ""); - - 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()) { - r.Error(ACK_ERROR_NO_EXIST, "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(r, error); - - DetachedSong song(path_utf8); - if (!song.LoadFile(path_fs)) { - r.Error(ACK_ERROR_NO_EXIST, "No such file"); - return CommandResult::ERROR; - } - - song_print_info(r, client.partition, song); - return CommandResult::OK; + if (!tag_stream_scan(uri, print_tag_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; } - if (uri_has_scheme(uri)) { - if (!uri_supported_scheme(uri)) { - r.Error(ACK_ERROR_NO_EXIST, "unsupported URI scheme"); - return CommandResult::ERROR; - } - - if (!tag_stream_scan(uri, print_tag_handler, &r)) { - r.Error(ACK_ERROR_NO_EXIST, "No such file"); - return CommandResult::ERROR; - } - - return CommandResult::OK; - } + return CommandResult::OK; +} +static CommandResult +handle_lsinfo_relative(Client &client, Response &r, const char *uri) +{ #ifdef ENABLE_DATABASE CommandResult result = handle_lsinfo2(client, uri, r); if (result != CommandResult::OK) return result; +#else + (void)client; #endif if (isRootDirectory(uri)) { @@ -219,6 +210,53 @@ handle_lsinfo(Client &client, Request args, Response &r) return CommandResult::OK; } +static CommandResult +handle_lsinfo_path(Client &client, Response &r, + const char *path_utf8, Path path_fs) +{ + DetachedSong song(path_utf8); + if (!song.LoadFile(path_fs)) { + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; + } + + song_print_info(r, client.partition, song); + return CommandResult::OK; +} + +CommandResult +handle_lsinfo(Client &client, Request args, Response &r) +{ + /* default is root directory */ + const auto uri = args.GetOptional(0, ""); + + Error error; + const auto located_uri = LocateUri(uri, &client, +#ifdef ENABLE_DATABASE + nullptr, +#endif + error); + + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); + + case LocatedUri::Type::ABSOLUTE: + return handle_lsinfo_absolute(r, located_uri.canonical_uri); + + case LocatedUri::Type::RELATIVE: + return handle_lsinfo_relative(client, r, + located_uri.canonical_uri); + + case LocatedUri::Type::PATH: + /* print information about an arbitrary local file */ + return handle_lsinfo_path(client, r, located_uri.canonical_uri, + located_uri.path); + } + + gcc_unreachable(); +} + #ifdef ENABLE_DATABASE static CommandResult diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index 1698e88df..7751aa26d 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -25,13 +25,14 @@ #include "db/Selection.hxx" #include "SongFilter.hxx" #include "SongLoader.hxx" +#include "DetachedSong.hxx" +#include "LocateUri.hxx" #include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" #include "client/Client.hxx" #include "client/Response.hxx" #include "Partition.hxx" #include "BulkEdit.hxx" -#include "ls.hxx" #include "util/ConstBuffer.hxx" #include "util/UriUtil.hxx" #include "util/NumberParser.hxx" @@ -42,15 +43,42 @@ #include <string.h> -static const char * -translate_uri(const char *uri) +static CommandResult +AddUri(Client &client, const LocatedUri &uri, Response &r) { - if (memcmp(uri, "file:///", 8) == 0) - /* drop the "file://", leave only an absolute path - (starting with a slash) */ - return uri + 7; + Error error; + DetachedSong *song = SongLoader(client).LoadSong(uri, error); + if (song == nullptr) + return print_error(r, error); + + auto &partition = client.partition; + unsigned id = partition.playlist.AppendSong(partition.pc, + std::move(*song), error); + delete song; + if (id == 0) + return print_error(r, error); - return uri; + return CommandResult::OK; +} + +static CommandResult +AddDatabaseSelection(Client &client, const char *uri, Response &r) +{ +#ifdef ENABLE_DATABASE + const ScopeBulkEdit bulk_edit(client.partition); + + const DatabaseSelection selection(uri, true); + Error error; + return AddFromDatabase(client.partition, selection, error) + ? CommandResult::OK + : print_error(r, error); +#else + (void)client; + (void)uri; + + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#endif } CommandResult @@ -65,36 +93,32 @@ handle_add(Client &client, Request args, Response &r) here */ uri = ""; - uri = translate_uri(uri); + Error error; + const auto located_uri = LocateUri(uri, &client, +#ifdef ENABLE_DATABASE + nullptr, +#endif + error); + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); - if (uri_has_scheme(uri) || PathTraitsUTF8::IsAbsolute(uri)) { - const SongLoader loader(client); - Error error; - unsigned id = client.partition.AppendURI(loader, uri, error); - if (id == 0) - return print_error(r, error); + case LocatedUri::Type::ABSOLUTE: + case LocatedUri::Type::PATH: + return AddUri(client, located_uri, r); - return CommandResult::OK; + case LocatedUri::Type::RELATIVE: + return AddDatabaseSelection(client, located_uri.canonical_uri, + r); } -#ifdef ENABLE_DATABASE - const ScopeBulkEdit bulk_edit(client.partition); - - const DatabaseSelection selection(uri, true); - Error error; - return AddFromDatabase(client.partition, selection, error) - ? CommandResult::OK - : print_error(r, error); -#else - r.Error(ACK_ERROR_NO_EXIST, "No database"); - return CommandResult::ERROR; -#endif + gcc_unreachable(); } CommandResult handle_addid(Client &client, Request args, Response &r) { - const char *const uri = translate_uri(args.front()); + const char *const uri = args.front(); const SongLoader loader(client); Error error; |