From da5ff779c6485e9e490515ac68f1e3b93fc90c90 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sun, 7 Feb 2021 21:56:18 +0100 Subject: [PATCH 01/21] python/build/libs.py: enable CURL/schannel support on Windows Closes https://github.com/MusicPlayerDaemon/MPD/issues/1031 --- NEWS | 2 ++ python/build/libs.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 9bb3f60a0..843f55db1 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ ver 0.22.5 (not yet released) - iso9660: another fix for unaligned reads * output - httpd: error handling on Windows improved +* Windows: + - enable https:// support (via Schannel) ver 0.22.4 (2021/01/21) * protocol diff --git a/python/build/libs.py b/python/build/libs.py index 517e892b3..e5277856c 100644 --- a/python/build/libs.py +++ b/python/build/libs.py @@ -407,6 +407,9 @@ curl = AutotoolsProject( '--disable-progress-meter', '--disable-alt-svc', '--without-gnutls', '--without-nss', '--without-libssh2', + + # native Windows SSL/TLS support, option ignored on non-Windows builds + '--with-schannel', ], patches='src/lib/curl/patches', From 78b43a993072d17cb9984f9b42c0406d2e54f00c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 13:00:09 +0100 Subject: [PATCH 02/21] doc/protocol.rst: document `add` on local socket Closes https://github.com/MusicPlayerDaemon/MPD/issues/1022 --- doc/protocol.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/protocol.rst b/doc/protocol.rst index 0c4c9ac6f..3d2c90f4d 100644 --- a/doc/protocol.rst +++ b/doc/protocol.rst @@ -677,6 +677,11 @@ Whenever possible, ids should be used. (directories add recursively). ``URI`` can also be a single file. + Clients that are connected via local socket may add arbitrary + local files (URI is an absolute path). Exmaple:: + + add "/home/foo/Music/bar.ogg" + .. _command_addid: :command:`addid {URI} [POSITION]` From 6d2b09ac2b4eb2e7d477e6fa128db7f057b2531c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 13:41:46 +0100 Subject: [PATCH 03/21] doc/developer.rst: update branch names --- doc/developer.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/developer.rst b/doc/developer.rst index a534c0ec0..8cbc8c3b0 100644 --- a/doc/developer.rst +++ b/doc/developer.rst @@ -68,11 +68,11 @@ There are two active branches in the git repository: - the "unstable" branch called ``master`` where new features are merged. This will become the next major release eventually. -- the "stable" branch (currently called ``v0.21.x``) where only bug +- the "stable" branch (currently called ``v0.22.x``) where only bug fixes are merged. -Once :program:`MPD` 0.22 is released, a new branch called ``v0.22.x`` -will be created for 0.22 bug-fix releases; after that, ``v0.21.x`` +Once :program:`MPD` 0.23 is released, a new branch called ``v0.23.x`` +will be created for 0.23 bug-fix releases; after that, ``v0.22.x`` will eventually cease to be maintained. After bug fixes have been added to the "stable" branch, it will be From def120aca4c6fd2dccdc9f36f64f66bdbaa58a8d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 15:58:33 +0100 Subject: [PATCH 04/21] output/pulse: eliminate the `pause` field It is useless, because we're always checking pa_stream_is_corked(). --- src/output/plugins/PulseOutputPlugin.cxx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index a2adf4e54..c615853d6 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -56,8 +56,6 @@ class PulseOutput final : AudioOutput { size_t writable; - bool pause; - /** * Was Interrupt() called? This will unblock Play(). It will * be reset by Cancel() and Pause(), as documented by the @@ -688,7 +686,6 @@ PulseOutput::Open(AudioFormat &audio_format) "pa_stream_connect_playback() has failed"); } - pause = false; interrupted = false; } @@ -780,7 +777,7 @@ PulseOutput::Delay() const noexcept Pulse::LockGuard lock(mainloop); auto result = std::chrono::steady_clock::duration::zero(); - if (pause && pa_stream_is_corked(stream) && + if (pa_stream_is_corked(stream) && pa_stream_get_state(stream) == PA_STREAM_READY) /* idle while paused */ result = std::chrono::seconds(1); @@ -796,8 +793,6 @@ PulseOutput::Play(const void *chunk, size_t size) Pulse::LockGuard lock(mainloop); - pause = false; - /* check if the stream is (already) connected */ WaitStream(); @@ -876,7 +871,6 @@ PulseOutput::Pause() Pulse::LockGuard lock(mainloop); - pause = true; interrupted = false; /* check if the stream is (already/still) connected */ From c2bc3704e1fc40107cffa550bb1475655a634c06 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 14:27:19 +0100 Subject: [PATCH 05/21] output/pulse: move code to virtual method Drain() Drain only if it was requested explicitly. --- src/output/plugins/PulseOutputPlugin.cxx | 29 +++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index c615853d6..a76562fef 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -111,6 +111,7 @@ public: [[nodiscard]] std::chrono::steady_clock::duration Delay() const noexcept override; size_t Play(const void *chunk, size_t size) override; + void Drain() override; void Cancel() noexcept override; bool Pause() override; @@ -696,17 +697,6 @@ PulseOutput::Close() noexcept Pulse::LockGuard lock(mainloop); - if (pa_stream_get_state(stream) == PA_STREAM_READY) { - pa_operation *o = - pa_stream_drain(stream, - pulse_output_stream_success_cb, this); - if (o == nullptr) { - LogPulseError(context, - "pa_stream_drain() has failed"); - } else - pulse_wait_for_operation(mainloop, o); - } - DeleteStream(); if (context != nullptr && @@ -835,6 +825,23 @@ PulseOutput::Play(const void *chunk, size_t size) return size; } +void +PulseOutput::Drain() +{ + Pulse::LockGuard lock(mainloop); + + if (pa_stream_get_state(stream) != PA_STREAM_READY) + return; + + pa_operation *o = + pa_stream_drain(stream, + pulse_output_stream_success_cb, this); + if (o == nullptr) + throw MakePulseError(context, "pa_stream_drain() failed"); + + pulse_wait_for_operation(mainloop, o); +} + void PulseOutput::Cancel() noexcept { From f1b8bcd6b26ff40fc49dab1b46f4a85e58eafeab Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 16:02:01 +0100 Subject: [PATCH 06/21] output/pulse: don't drain if stream is suspended or corked In this state, we can't make any progress. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1084 --- NEWS | 1 + src/output/plugins/PulseOutputPlugin.cxx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 843f55db1..7b0615128 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ ver 0.22.5 (not yet released) - iso9660: another fix for unaligned reads * output - httpd: error handling on Windows improved + - pulse: fix deadlock with "always_on" * Windows: - enable https:// support (via Schannel) diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index a76562fef..47c5fe025 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -830,7 +830,9 @@ PulseOutput::Drain() { Pulse::LockGuard lock(mainloop); - if (pa_stream_get_state(stream) != PA_STREAM_READY) + if (pa_stream_get_state(stream) != PA_STREAM_READY || + pa_stream_is_suspended(stream) || + pa_stream_is_corked(stream)) return; pa_operation *o = From efde78db777c2d0f8f956196d8022777838ce9e1 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 16:15:27 +0100 Subject: [PATCH 07/21] output/Thread: skip drain calls if there is no data to be played Keep track of whether there is data being played, and don't call AudioOutput::Drain() after Cancel() has been called already. --- src/output/Control.hxx | 8 ++++++++ src/output/Thread.cxx | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/output/Control.hxx b/src/output/Control.hxx index ba61ced6e..734b29e67 100644 --- a/src/output/Control.hxx +++ b/src/output/Control.hxx @@ -181,6 +181,14 @@ class AudioOutputControl { */ bool open = false; + /** + * Is the device currently playing, i.e. is its buffer + * (likely) non-empty? If not, then it will never be drained. + * + * This field is only valid while the output is open. + */ + bool playing; + /** * Is the device paused? i.e. the output thread is in the * ao_pause() loop. diff --git a/src/output/Thread.cxx b/src/output/Thread.cxx index 3da07c6b2..c0aed30b0 100644 --- a/src/output/Thread.cxx +++ b/src/output/Thread.cxx @@ -53,7 +53,7 @@ AudioOutputControl::InternalOpen2(const AudioFormat in_audio_format) if (open && cf != output->filter_audio_format) /* if the filter's output format changes, the output must be reopened as well */ - InternalCloseOutput(true); + InternalCloseOutput(playing); output->filter_audio_format = cf; @@ -64,6 +64,7 @@ AudioOutputControl::InternalOpen2(const AudioFormat in_audio_format) } open = true; + playing = false; } else if (in_audio_format != output->out_audio_format) { /* reconfigure the final ConvertFilter for its new input AudioFormat */ @@ -285,6 +286,9 @@ AudioOutputControl::PlayChunk(std::unique_lock &lock) noexcept assert(nbytes % output->out_audio_format.GetFrameSize() == 0); source.ConsumeData(nbytes); + + /* there's data to be drained from now on */ + playing = true; } return true; @@ -371,6 +375,9 @@ AudioOutputControl::InternalPause(std::unique_lock &lock) noexcept } skip_delay = true; + + /* ignore drain commands until we got something new to play */ + playing = false; } static void @@ -390,6 +397,10 @@ PlayFull(FilteredAudioOutput &output, ConstBuffer _buffer) inline void AudioOutputControl::InternalDrain() noexcept { + /* after this method finishes, there's nothing left to be + drained */ + playing = false; + try { /* flush the filter and play its remaining output */ @@ -518,6 +529,7 @@ AudioOutputControl::Task() noexcept source.Cancel(); if (open) { + playing = false; const ScopeUnlock unlock(mutex); output->Cancel(); } From d4d06da2f8ed7364ae897bb9527923c704c0892e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 17:34:03 +0100 Subject: [PATCH 08/21] db/simple: fix dangling LightSong::tag reference in moved ExportedSong After commit 1afa33c3c766af2, an old bug was revealed: SimpleDatabase::GetSong() constructs an ExportedSong instance by moving the return value of Song::Export(), which causes the LightSong::tag field to be dangling on the moved-from ExportedSong::tag_buffer. This broke tags from CUE sheets. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1070 --- NEWS | 2 ++ src/db/plugins/simple/ExportedSong.hxx | 9 +++++++++ src/song/LightSong.hxx | 13 +++++++++++++ 3 files changed, 24 insertions(+) diff --git a/NEWS b/NEWS index 7b0615128..80ae7c3c7 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ ver 0.22.5 (not yet released) +* database + - simple: fix missing CUE sheet metadata in "addid" command * tags - id: translate TPE3 to Conductor, not Performer * archive diff --git a/src/db/plugins/simple/ExportedSong.hxx b/src/db/plugins/simple/ExportedSong.hxx index 886be078b..37e4109b5 100644 --- a/src/db/plugins/simple/ExportedSong.hxx +++ b/src/db/plugins/simple/ExportedSong.hxx @@ -37,6 +37,15 @@ public: ExportedSong(const char *_uri, Tag &&_tag) noexcept :LightSong(_uri, tag_buffer), tag_buffer(std::move(_tag)) {} + + /* this custom move constructor is necessary so LightSong::tag + points to this instance's #Tag field instead of leaving a + dangling reference to the source object's #Tag field */ + ExportedSong(ExportedSong &&src) noexcept + :LightSong(src, tag_buffer), + tag_buffer(std::move(src.tag_buffer)) {} + + ExportedSong &operator=(ExportedSong &&) = delete; }; #endif diff --git a/src/song/LightSong.hxx b/src/song/LightSong.hxx index ee372576a..06f0a41f9 100644 --- a/src/song/LightSong.hxx +++ b/src/song/LightSong.hxx @@ -88,6 +88,19 @@ struct LightSong { LightSong(const char *_uri, const Tag &_tag) noexcept :uri(_uri), tag(_tag) {} + /** + * A copy constructor which copies all fields, but only sets + * the tag to a caller-provided reference. This is used by + * the #ExportedSong move constructor. + */ + LightSong(const LightSong &src, const Tag &_tag) noexcept + :directory(src.directory), uri(src.uri), + real_uri(src.real_uri), + tag(_tag), + mtime(src.mtime), + start_time(src.start_time), end_time(src.end_time), + audio_format(src.audio_format) {} + gcc_pure std::string GetURI() const noexcept { if (directory == nullptr) From ca02fb7782bead0befff41d1ea61e678d7f1e9f9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 17:12:30 +0100 Subject: [PATCH 09/21] android/AndroidManifest.xml: enable requestLegacyExternalStorage This is a workaround for the new scoped storage design in Android 11: https://developer.android.com/about/versions/11/privacy/storage This needs a proper solution eventually, but this quick fix will do until we change "targetSdkVersion" to 30. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1061 --- NEWS | 2 ++ android/AndroidManifest.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 80ae7c3c7..e7fdf2154 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ ver 0.22.5 (not yet released) - pulse: fix deadlock with "always_on" * Windows: - enable https:// support (via Schannel) +* Android + - work around "Permission denied" on mpd.conf ver 0.22.4 (2021/01/21) * protocol diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 364d571e0..1baa1b9cf 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -19,6 +19,7 @@ From d9e5d5ff5b8e0239eed307e94a72c3520c01fad3 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Wed, 2 Dec 2020 06:43:49 +0800 Subject: [PATCH 10/21] src/win32: Add error message for NO_ERROR --- src/win32/HResult.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/win32/HResult.hxx b/src/win32/HResult.hxx index a4fbcb8b6..2bc851f73 100644 --- a/src/win32/HResult.hxx +++ b/src/win32/HResult.hxx @@ -59,6 +59,7 @@ case x: C(E_INVALIDARG); C(E_OUTOFMEMORY); C(E_POINTER); + C(NO_ERROR); #undef C } return std::string_view(); From 7ef489e0574f9ee3a2c2bd71ca5d394d88281a56 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Wed, 2 Dec 2020 07:20:03 +0800 Subject: [PATCH 11/21] src/win32: run clang-format --- src/win32/ComPtr.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/ComPtr.hxx b/src/win32/ComPtr.hxx index 6f0f37050..325c06c10 100644 --- a/src/win32/ComPtr.hxx +++ b/src/win32/ComPtr.hxx @@ -112,6 +112,6 @@ template void swap(ComPtr &lhs, ComPtr &rhs) noexcept { lhs.swap(rhs); } -} +} // namespace std #endif From 481c330c17b2457d001b03c27edf6f0cde446278 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Wed, 2 Dec 2020 07:36:40 +0800 Subject: [PATCH 12/21] src/output: Set thread name for Wasapi output thread --- src/output/plugins/WasapiOutputPlugin.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/output/plugins/WasapiOutputPlugin.cxx b/src/output/plugins/WasapiOutputPlugin.cxx index 898e2e910..ce91f4e5b 100644 --- a/src/output/plugins/WasapiOutputPlugin.cxx +++ b/src/output/plugins/WasapiOutputPlugin.cxx @@ -24,6 +24,7 @@ #include "mixer/MixerList.hxx" #include "thread/Cond.hxx" #include "thread/Mutex.hxx" +#include "thread/Name.hxx" #include "thread/Thread.hxx" #include "util/AllocatedString.hxx" #include "util/Domain.hxx" @@ -231,6 +232,7 @@ IAudioClient *wasapi_output_get_client(WasapiOutput &output) noexcept { } void WasapiOutputThread::Work() noexcept { + SetThreadName("Wasapi Output Worker"); FormatDebug(wasapi_output_domain, "Working thread started"); try { com.emplace(); From 103194e32d1d86042ac41a581386289834469e20 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 19:43:53 +0100 Subject: [PATCH 13/21] protocol/RangeArg: add missing `noexcept` --- src/protocol/RangeArg.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/RangeArg.hxx b/src/protocol/RangeArg.hxx index a116920b1..a55a2755c 100644 --- a/src/protocol/RangeArg.hxx +++ b/src/protocol/RangeArg.hxx @@ -25,7 +25,7 @@ struct RangeArg { unsigned start, end; - static constexpr RangeArg All() { + static constexpr RangeArg All() noexcept { return { 0, std::numeric_limits::max() }; } From 6e1940e93060ea8e1b83bea932447bdd693a598e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 19:55:55 +0100 Subject: [PATCH 14/21] protocol/RangeArg: add static method OpenEnded() --- src/protocol/ArgParser.cxx | 2 +- src/protocol/RangeArg.hxx | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx index 98cf74150..deaa8f08f 100644 --- a/src/protocol/ArgParser.cxx +++ b/src/protocol/ArgParser.cxx @@ -94,7 +94,7 @@ ParseCommandArgRange(const char *s) s); if (test == test2) - value = std::numeric_limits::max(); + return RangeArg::OpenEnded(range.start); if (value < 0) throw FormatProtocolError(ACK_ERROR_ARG, diff --git a/src/protocol/RangeArg.hxx b/src/protocol/RangeArg.hxx index a55a2755c..ab00dc990 100644 --- a/src/protocol/RangeArg.hxx +++ b/src/protocol/RangeArg.hxx @@ -25,8 +25,15 @@ struct RangeArg { unsigned start, end; + /** + * Construct an open-ended range starting at the given index. + */ + static constexpr RangeArg OpenEnded(unsigned start) noexcept { + return { start, std::numeric_limits::max() }; + } + static constexpr RangeArg All() noexcept { - return { 0, std::numeric_limits::max() }; + return OpenEnded(0); } constexpr bool operator==(RangeArg other) const noexcept { From ad059d58045355913e6ba77f1e938f46cfd7739b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 19:56:46 +0100 Subject: [PATCH 15/21] protocol/RangeArg: add method IsOpenEnded() --- src/protocol/RangeArg.hxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/protocol/RangeArg.hxx b/src/protocol/RangeArg.hxx index ab00dc990..246210b83 100644 --- a/src/protocol/RangeArg.hxx +++ b/src/protocol/RangeArg.hxx @@ -44,6 +44,10 @@ struct RangeArg { return !(*this == other); } + constexpr bool IsOpenEnded() const noexcept { + return end == All().end; + } + constexpr bool IsAll() const noexcept { return *this == All(); } From b57eeaa7203fe7216fcd10f4a649b8f76932602e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 19:44:20 +0100 Subject: [PATCH 16/21] protocol/RangeArg: add static method Single() --- src/protocol/ArgParser.cxx | 2 +- src/protocol/RangeArg.hxx | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx index deaa8f08f..2cfac9b33 100644 --- a/src/protocol/ArgParser.cxx +++ b/src/protocol/ArgParser.cxx @@ -107,7 +107,7 @@ ParseCommandArgRange(const char *s) range.end = (unsigned)value; } else { - range.end = (unsigned)value + 1; + return RangeArg::Single(range.start); } return range; diff --git a/src/protocol/RangeArg.hxx b/src/protocol/RangeArg.hxx index 246210b83..5f846350c 100644 --- a/src/protocol/RangeArg.hxx +++ b/src/protocol/RangeArg.hxx @@ -36,6 +36,13 @@ struct RangeArg { return OpenEnded(0); } + /** + * Construct an instance describing exactly one index. + */ + static constexpr RangeArg Single(unsigned i) noexcept { + return { i, i + 1 }; + } + constexpr bool operator==(RangeArg other) const noexcept { return start == other.start && end == other.end; } From 19a46064e91ece0c3e9a88a6ddc8efe4cdd15032 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 19:46:56 +0100 Subject: [PATCH 17/21] protocol/RangeArg: add methods IsWellFormed(), IsEmpty(), HasAtLeast(), Count() --- src/protocol/RangeArg.hxx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/protocol/RangeArg.hxx b/src/protocol/RangeArg.hxx index 5f846350c..347fa0aea 100644 --- a/src/protocol/RangeArg.hxx +++ b/src/protocol/RangeArg.hxx @@ -59,9 +59,37 @@ struct RangeArg { return *this == All(); } + constexpr bool IsWellFormed() const noexcept { + return start <= end; + } + + /** + * Is this range empty? A malformed range also counts as + * "empty" for this method. + */ + constexpr bool IsEmpty() const noexcept { + return start >= end; + } + + /** + * Check if the range contains at least this number of items. + * Unlike Count(), this allows the object to be malformed. + */ + constexpr bool HasAtLeast(unsigned n) const noexcept { + return start + n <= end; + } + constexpr bool Contains(unsigned i) const noexcept { return i >= start && i < end; } + + /** + * Count the number of items covered by this range. This requires the + * object to be well-formed. + */ + constexpr unsigned Count() const noexcept { + return end - start; + } }; #endif From a6c10e9a1c37defe67c056ada3a41f62c404d5f7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 20:25:41 +0100 Subject: [PATCH 18/21] protocol/ArgParser: check for invalid ranges Catch errors like that early, before invalid ranges get passed to internal MPD subsystems. --- NEWS | 2 ++ src/protocol/ArgParser.cxx | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/NEWS b/NEWS index e7fdf2154..9bda29812 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ ver 0.22.5 (not yet released) +* protocol + - error for malformed ranges instead of ignoring silently * database - simple: fix missing CUE sheet metadata in "addid" command * tags diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx index 2cfac9b33..b5d05b473 100644 --- a/src/protocol/ArgParser.cxx +++ b/src/protocol/ArgParser.cxx @@ -110,6 +110,10 @@ ParseCommandArgRange(const char *s) return RangeArg::Single(range.start); } + if (!range.IsWellFormed()) + throw FormatProtocolError(ACK_ERROR_ARG, + "Malformed range: %s", s); + return range; } From 6b1d264b352a17bf0ed59f79960359cd4949d8df Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 20:51:12 +0100 Subject: [PATCH 19/21] command/queue: better error message for open-ended range with "move" The "move" command doesn't allow open-ended ranges because they don't make a lot of sense; moving an open-ended range is only possible if the destination index is before the range, and in that case, the client should be well aware how many songs there are. Closes https://github.com/MusicPlayerDaemon/MPD/pull/1057 --- NEWS | 1 + src/command/QueueCommands.cxx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/NEWS b/NEWS index 9bda29812..7a85fb437 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ver 0.22.5 (not yet released) * protocol - error for malformed ranges instead of ignoring silently + - better error message for open-ended range with "move" * database - simple: fix missing CUE sheet metadata in "addid" command * tags diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index c77b62b3e..ba8c5a423 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -326,6 +326,11 @@ CommandResult handle_move(Client &client, Request args, [[maybe_unused]] Response &r) { RangeArg range = args.ParseRange(0); + if (range.IsOpenEnded()) { + r.Error(ACK_ERROR_ARG, "Open-ended range not supported"); + return CommandResult::ERROR; + } + int to = args.ParseInt(1); client.GetPartition().MoveRange(range.start, range.end, to); return CommandResult::OK; From 28a5cdf319b8b66e6defe1c4db4e2ff2e8e82ae7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 21:11:34 +0100 Subject: [PATCH 20/21] android/meson.build: update the SDK platform to 29 Needed for `requestLegacyExternalStorage` (commit ca02fb7782bea). --- android/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/meson.build b/android/meson.build index ee1f7cd31..a8caa0243 100644 --- a/android/meson.build +++ b/android/meson.build @@ -5,8 +5,8 @@ android_ndk = get_option('android_ndk') android_sdk = get_option('android_sdk') android_abi = get_option('android_abi') -android_sdk_build_tools_version = '27.0.0' -android_sdk_platform = 'android-23' +android_sdk_build_tools_version = '29.0.3' +android_sdk_platform = 'android-29' android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version) android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform) From f8be403c3428f595250fd10c1ecf5d1a273d3aed Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 15 Feb 2021 21:18:18 +0100 Subject: [PATCH 21/21] release v0.22.5 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7a85fb437..13d1ddd96 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ver 0.22.5 (not yet released) +ver 0.22.5 (2021/02/15) * protocol - error for malformed ranges instead of ignoring silently - better error message for open-ended range with "move"