diff options
Diffstat (limited to 'src/command/QueueCommands.cxx')
-rw-r--r-- | src/command/QueueCommands.cxx | 216 |
1 files changed, 132 insertions, 84 deletions
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index a987e1bc9..d0b789eb1 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,19 +20,21 @@ #include "config.h" #include "QueueCommands.hxx" #include "CommandError.hxx" -#include "DatabaseQueue.hxx" +#include "db/DatabaseQueue.hxx" +#include "db/Selection.hxx" #include "SongFilter.hxx" -#include "DatabaseSelection.hxx" -#include "Playlist.hxx" +#include "SongLoader.hxx" +#include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" -#include "ClientFile.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "Partition.hxx" #include "BulkEdit.hxx" #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "ls.hxx" +#include "util/ConstBuffer.hxx" #include "util/UriUtil.hxx" +#include "util/NumberParser.hxx" #include "util/Error.hxx" #include "fs/AllocatedPath.hxx" @@ -40,40 +42,49 @@ #include <string.h> -CommandResult -handle_add(Client &client, gcc_unused int argc, char *argv[]) +static const char * +translate_uri(Client &client, const char *uri) { - char *uri = argv[1]; + if (memcmp(uri, "file:///", 8) == 0) + /* drop the "file://", leave only an absolute path + (starting with a slash) */ + return uri + 7; + + if (PathTraitsUTF8::IsAbsolute(uri)) { + command_error(client, ACK_ERROR_NO_EXIST, "Malformed URI"); + return nullptr; + } - if (memcmp(uri, "file:///", 8) == 0) { - const char *path_utf8 = uri + 7; - const auto path_fs = AllocatedPath::FromUTF8(path_utf8); + return uri; +} - if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); - return CommandResult::ERROR; - } +CommandResult +handle_add(Client &client, gcc_unused unsigned argc, char *argv[]) +{ + const char *uri = argv[1]; + if (memcmp(uri, "/", 2) == 0) + /* this URI is malformed, but some clients are buggy + and use "add /" to add the whole database, which + was never intended to work, but once did; in order + to retain backwards compatibility, work around this + here */ + uri = ""; + + uri = translate_uri(client, uri); + if (uri == nullptr) + return CommandResult::ERROR; + if (uri_has_scheme(uri) || PathTraitsUTF8::IsAbsolute(uri)) { + const SongLoader loader(client); Error error; - if (!client_allow_file(client, path_fs, error)) + unsigned id = client.partition.AppendURI(loader, uri, error); + if (id == 0) return print_error(client, error); - auto result = client.partition.AppendFile(path_utf8); - return print_playlist_result(client, result); - } - - if (uri_has_scheme(uri)) { - if (!uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); - return CommandResult::ERROR; - } - - auto result = client.partition.AppendURI(uri); - return print_playlist_result(client, result); + return CommandResult::OK; } +#ifdef ENABLE_DATABASE const ScopeBulkEdit bulk_edit(client.partition); const DatabaseSelection selection(uri, true); @@ -81,48 +92,30 @@ handle_add(Client &client, gcc_unused int argc, char *argv[]) return AddFromDatabase(client.partition, selection, error) ? CommandResult::OK : print_error(client, error); +#else + command_error(client, ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#endif } CommandResult -handle_addid(Client &client, int argc, char *argv[]) +handle_addid(Client &client, unsigned argc, char *argv[]) { - char *uri = argv[1]; - unsigned added_id; - PlaylistResult result; - - if (memcmp(uri, "file:///", 8) == 0) { - const char *path_utf8 = uri + 7; - const auto path_fs = AllocatedPath::FromUTF8(path_utf8); - - if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client_allow_file(client, path_fs, error)) - return print_error(client, error); - - result = client.partition.AppendFile(path_utf8, &added_id); - } else { - if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); - return CommandResult::ERROR; - } - - result = client.partition.AppendURI(uri, &added_id); - } + const char *const uri = translate_uri(client, argv[1]); + if (uri == nullptr) + return CommandResult::ERROR; - if (result != PlaylistResult::SUCCESS) - return print_playlist_result(client, result); + const SongLoader loader(client); + Error error; + unsigned added_id = client.partition.AppendURI(loader, uri, error); + if (added_id == 0) + return print_error(client, error); if (argc == 3) { unsigned to; if (!check_unsigned(client, &to, argv[2])) return CommandResult::ERROR; - result = client.partition.MoveId(added_id, to); + PlaylistResult result = client.partition.MoveId(added_id, to); if (result != PlaylistResult::SUCCESS) { CommandResult ret = print_playlist_result(client, result); @@ -135,8 +128,61 @@ handle_addid(Client &client, int argc, char *argv[]) return CommandResult::OK; } +/** + * Parse a string in the form "START:END", both being (optional) + * fractional non-negative time offsets in seconds. Returns both in + * integer milliseconds. Omitted values are zero. + */ +static bool +parse_time_range(const char *p, SongTime &start_r, SongTime &end_r) +{ + char *endptr; + + const float start = ParseFloat(p, &endptr); + if (*endptr != ':' || start < 0) + return false; + + start_r = endptr > p + ? SongTime::FromS(start) + : SongTime::zero(); + + p = endptr + 1; + + const float end = ParseFloat(p, &endptr); + if (*endptr != 0 || end < 0) + return false; + + end_r = endptr > p + ? SongTime::FromS(end) + : SongTime::zero(); + + return end_r.IsZero() || end_r > start_r; +} + +CommandResult +handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[]) +{ + unsigned id; + if (!check_unsigned(client, &id, argv[1])) + return CommandResult::ERROR; + + SongTime start, end; + if (!parse_time_range(argv[2], start, end)) { + command_error(client, ACK_ERROR_ARG, "Bad range"); + return CommandResult::ERROR; + } + + Error error; + if (!client.partition.playlist.SetSongIdRange(client.partition.pc, + id, start, end, + error)) + return print_error(client, error); + + return CommandResult::OK; +} + CommandResult -handle_delete(Client &client, gcc_unused int argc, char *argv[]) +handle_delete(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned start, end; @@ -148,7 +194,7 @@ handle_delete(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_deleteid(Client &client, gcc_unused int argc, char *argv[]) +handle_deleteid(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned id; @@ -161,7 +207,7 @@ handle_deleteid(Client &client, gcc_unused int argc, char *argv[]) CommandResult handle_playlist(Client &client, - gcc_unused int argc, gcc_unused char *argv[]) + gcc_unused unsigned argc, gcc_unused char *argv[]) { playlist_print_uris(client, client.playlist); return CommandResult::OK; @@ -169,7 +215,7 @@ handle_playlist(Client &client, CommandResult handle_shuffle(gcc_unused Client &client, - gcc_unused int argc, gcc_unused char *argv[]) + gcc_unused unsigned argc, gcc_unused char *argv[]) { unsigned start = 0, end = client.playlist.queue.GetLength(); if (argc == 2 && !check_range(client, &start, &end, argv[1])) @@ -181,14 +227,14 @@ handle_shuffle(gcc_unused Client &client, CommandResult handle_clear(gcc_unused Client &client, - gcc_unused int argc, gcc_unused char *argv[]) + gcc_unused unsigned argc, gcc_unused char *argv[]) { client.partition.ClearQueue(); return CommandResult::OK; } CommandResult -handle_plchanges(Client &client, gcc_unused int argc, char *argv[]) +handle_plchanges(Client &client, gcc_unused unsigned argc, char *argv[]) { uint32_t version; @@ -200,7 +246,7 @@ handle_plchanges(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_plchangesposid(Client &client, gcc_unused int argc, char *argv[]) +handle_plchangesposid(Client &client, gcc_unused unsigned argc, char *argv[]) { uint32_t version; @@ -212,7 +258,7 @@ handle_plchangesposid(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_playlistinfo(Client &client, int argc, char *argv[]) +handle_playlistinfo(Client &client, unsigned argc, char *argv[]) { unsigned start = 0, end = std::numeric_limits<unsigned>::max(); bool ret; @@ -229,7 +275,7 @@ handle_playlistinfo(Client &client, int argc, char *argv[]) } CommandResult -handle_playlistid(Client &client, int argc, char *argv[]) +handle_playlistid(Client &client, unsigned argc, char *argv[]) { if (argc >= 2) { unsigned id; @@ -249,11 +295,13 @@ handle_playlistid(Client &client, int argc, char *argv[]) } static CommandResult -handle_playlist_match(Client &client, int argc, char *argv[], +handle_playlist_match(Client &client, unsigned argc, char *argv[], bool fold_case) { + ConstBuffer<const char *> args(argv + 1, argc - 1); + SongFilter filter; - if (!filter.Parse(argc - 1, argv + 1, fold_case)) { + if (!filter.Parse(args, fold_case)) { command_error(client, ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } @@ -263,19 +311,19 @@ handle_playlist_match(Client &client, int argc, char *argv[], } CommandResult -handle_playlistfind(Client &client, int argc, char *argv[]) +handle_playlistfind(Client &client, unsigned argc, char *argv[]) { return handle_playlist_match(client, argc, argv, false); } CommandResult -handle_playlistsearch(Client &client, int argc, char *argv[]) +handle_playlistsearch(Client &client, unsigned argc, char *argv[]) { return handle_playlist_match(client, argc, argv, true); } CommandResult -handle_prio(Client &client, int argc, char *argv[]) +handle_prio(Client &client, unsigned argc, char *argv[]) { unsigned priority; @@ -288,7 +336,7 @@ handle_prio(Client &client, int argc, char *argv[]) return CommandResult::ERROR; } - for (int i = 2; i < argc; ++i) { + for (unsigned i = 2; i < argc; ++i) { unsigned start_position, end_position; if (!check_range(client, &start_position, &end_position, argv[i])) @@ -306,7 +354,7 @@ handle_prio(Client &client, int argc, char *argv[]) } CommandResult -handle_prioid(Client &client, int argc, char *argv[]) +handle_prioid(Client &client, unsigned argc, char *argv[]) { unsigned priority; @@ -319,7 +367,7 @@ handle_prioid(Client &client, int argc, char *argv[]) return CommandResult::ERROR; } - for (int i = 2; i < argc; ++i) { + for (unsigned i = 2; i < argc; ++i) { unsigned song_id; if (!check_unsigned(client, &song_id, argv[i])) return CommandResult::ERROR; @@ -334,7 +382,7 @@ handle_prioid(Client &client, int argc, char *argv[]) } CommandResult -handle_move(Client &client, gcc_unused int argc, char *argv[]) +handle_move(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned start, end; int to; @@ -350,7 +398,7 @@ handle_move(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_moveid(Client &client, gcc_unused int argc, char *argv[]) +handle_moveid(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned id; int to; @@ -364,7 +412,7 @@ handle_moveid(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_swap(Client &client, gcc_unused int argc, char *argv[]) +handle_swap(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned song1, song2; @@ -379,7 +427,7 @@ handle_swap(Client &client, gcc_unused int argc, char *argv[]) } CommandResult -handle_swapid(Client &client, gcc_unused int argc, char *argv[]) +handle_swapid(Client &client, gcc_unused unsigned argc, char *argv[]) { unsigned id1, id2; |