From 945ed2610a0ae2b68288cf4f7b7a6cd4c1903bb5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 27 Oct 2020 18:34:39 +0100 Subject: [PATCH 01/15] increment version number to 0.22.2 --- NEWS | 2 ++ doc/conf.py | 2 +- meson.build | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 15bb01153..8d45ce5b7 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +ver 0.22.2 (not yet released) + ver 0.22.1 (2020/10/17) * decoder - opus: apply the OpusHead output gain even if there is no EBU R128 tag diff --git a/doc/conf.py b/doc/conf.py index 642ab0970..fb9443dd3 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -38,7 +38,7 @@ author = 'Max Kellermann' # built documents. # # The short X.Y version. -version = '0.22.1' +version = '0.22.2' # The full version, including alpha/beta/rc tags. release = version diff --git a/meson.build b/meson.build index fe37628a1..ecf2fe0e7 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'mpd', ['c', 'cpp'], - version: '0.22.1', + version: '0.22.2', meson_version: '>= 0.49.0', default_options: [ 'c_std=c99', From 6f1a4a73b754810408cfbb449d0c71e63b762e82 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 27 Oct 2020 18:58:25 +0100 Subject: [PATCH 02/15] fs/Traits: add GetFilenameSuffix() --- src/fs/Traits.hxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx index da899df63..b61de1833 100644 --- a/src/fs/Traits.hxx +++ b/src/fs/Traits.hxx @@ -88,6 +88,14 @@ struct PathTraitsFS { #endif } + gcc_pure + static const_pointer GetFilenameSuffix(const_pointer filename) noexcept { + const_pointer dot = StringFindLast(filename, '.'); + return dot != nullptr && dot > filename && dot[1] != 0 + ? dot + 1 + : nullptr; + } + #ifdef _WIN32 gcc_pure gcc_nonnull_all static constexpr bool IsDrive(const_pointer p) noexcept { @@ -199,6 +207,14 @@ struct PathTraitsUTF8 { return std::strrchr(p, SEPARATOR); } + gcc_pure + static const_pointer GetFilenameSuffix(const_pointer filename) noexcept { + const_pointer dot = StringFindLast(filename, '.'); + return dot != nullptr && dot > filename && dot[1] != 0 + ? dot + 1 + : nullptr; + } + #ifdef _WIN32 gcc_pure gcc_nonnull_all static constexpr bool IsDrive(const_pointer p) noexcept { From 0779333064b2b0f752dfb1d81259f7d24e9a7317 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 27 Oct 2020 19:02:46 +0100 Subject: [PATCH 03/15] db/update/Walk: adjust lamba indent --- src/db/update/Walk.cxx | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 7526c6b6f..87304df62 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -69,46 +69,46 @@ UpdateWalk::RemoveExcludedFromDirectory(Directory &directory, const ScopeDatabaseLock protect; directory.ForEachChildSafe([&](Directory &child){ - const auto name_fs = - AllocatedPath::FromUTF8(child.GetName()); + const auto name_fs = + AllocatedPath::FromUTF8(child.GetName()); - if (name_fs.IsNull() || exclude_list.Check(name_fs)) { - editor.DeleteDirectory(&child); - modified = true; - } - }); + if (name_fs.IsNull() || exclude_list.Check(name_fs)) { + editor.DeleteDirectory(&child); + modified = true; + } + }); directory.ForEachSongSafe([&](Song &song){ - assert(&song.parent == &directory); + assert(&song.parent == &directory); - const auto name_fs = AllocatedPath::FromUTF8(song.filename); - if (name_fs.IsNull() || exclude_list.Check(name_fs)) { - editor.DeleteSong(directory, &song); - modified = true; - } - }); + const auto name_fs = AllocatedPath::FromUTF8(song.filename); + if (name_fs.IsNull() || exclude_list.Check(name_fs)) { + editor.DeleteSong(directory, &song); + modified = true; + } + }); } inline void UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept { directory.ForEachChildSafe([&](Directory &child){ - if (child.IsMount() || DirectoryExists(storage, child)) - return; + if (child.IsMount() || DirectoryExists(storage, child)) + return; - editor.LockDeleteDirectory(&child); + editor.LockDeleteDirectory(&child); - modified = true; - }); + modified = true; + }); directory.ForEachSongSafe([&](Song &song){ - if (!directory_child_is_regular(storage, directory, - song.filename)) { - editor.LockDeleteSong(directory, &song); + if (!directory_child_is_regular(storage, directory, + song.filename)) { + editor.LockDeleteSong(directory, &song); - modified = true; - } - }); + modified = true; + } + }); for (auto i = directory.playlists.begin(), end = directory.playlists.end(); From ecaa51e322095dce9ccc91984c605ffd8dd994ef Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 27 Oct 2020 19:00:46 +0100 Subject: [PATCH 04/15] db/simple: purge special directories for unavailable plugins on update --- NEWS | 2 + src/db/meson.build | 1 + src/db/plugins/simple/Directory.hxx | 7 +++ src/db/update/SpecialDirectory.cxx | 74 +++++++++++++++++++++++++++++ src/db/update/Walk.cxx | 6 ++- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/db/update/SpecialDirectory.cxx diff --git a/NEWS b/NEWS index 8d45ce5b7..7700571c3 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ ver 0.22.2 (not yet released) +* database + - simple: purge virtual directories for unavailable plugins on update ver 0.22.1 (2020/10/17) * decoder diff --git a/src/db/meson.build b/src/db/meson.build index 2a0c8a5bd..69bf50cd6 100644 --- a/src/db/meson.build +++ b/src/db/meson.build @@ -31,6 +31,7 @@ db_glue_sources = [ 'update/Remove.cxx', 'update/ExcludeList.cxx', 'update/VirtualDirectory.cxx', + 'update/SpecialDirectory.cxx', 'DatabaseGlue.cxx', 'Configured.cxx', 'DatabaseSong.cxx', diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx index 7062e609c..ea14420d0 100644 --- a/src/db/plugins/simple/Directory.hxx +++ b/src/db/plugins/simple/Directory.hxx @@ -132,6 +132,13 @@ public: return mounted_database != nullptr; } + /** + * Checks whether this is a "special" directory + * (e.g. #DEVICE_PLAYLIST) and whether the underlying plugin + * is available. + */ + bool IsPluginAvailable() const noexcept; + /** * Remove this #Directory object from its parent and free it. This * must not be called with the root Directory. diff --git a/src/db/update/SpecialDirectory.cxx b/src/db/update/SpecialDirectory.cxx new file mode 100644 index 000000000..49c025d18 --- /dev/null +++ b/src/db/update/SpecialDirectory.cxx @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2020 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 "db/plugins/simple/Directory.hxx" +#include "archive/ArchiveList.hxx" +#include "decoder/DecoderList.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "fs/Traits.hxx" + +gcc_pure +static bool +HaveArchivePluginForFilename(const char *filename) noexcept +{ +#ifdef ENABLE_ARCHIVE + const char *suffix = PathTraitsUTF8::GetFilenameSuffix(filename); + return suffix != nullptr && + archive_plugin_from_suffix(suffix) != nullptr; +#else + (void)filename; + return false; +#endif +} + +gcc_pure +static bool +HaveContainerPluginForFilename(const char *filename) noexcept +{ + const char *suffix = PathTraitsUTF8::GetFilenameSuffix(filename); + return suffix != nullptr && + // TODO: check if this plugin really supports containers + decoder_plugins_supports_suffix(suffix); +} + +gcc_pure +static bool +HavePlaylistPluginForFilename(const char *filename) noexcept +{ + const char *suffix = PathTraitsUTF8::GetFilenameSuffix(filename); + return suffix != nullptr && playlist_suffix_supported(suffix); +} + +bool +Directory::IsPluginAvailable() const noexcept +{ + switch (device) { + case DEVICE_INARCHIVE: + return HaveArchivePluginForFilename(GetName()); + + case DEVICE_CONTAINER: + return HaveContainerPluginForFilename(GetName()); + + case DEVICE_PLAYLIST: + return HavePlaylistPluginForFilename(GetName()); + + default: + return true; + } +} diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 87304df62..6ac12776e 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -93,7 +93,11 @@ inline void UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept { directory.ForEachChildSafe([&](Directory &child){ - if (child.IsMount() || DirectoryExists(storage, child)) + if (child.IsMount()) + return; + + if (DirectoryExists(storage, child) && + child.IsPluginAvailable()) return; editor.LockDeleteDirectory(&child); From ee802867dfa5d03b3c5e0fe4cebdaf2f6280c15b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 14:23:01 +0100 Subject: [PATCH 05/15] db/update/Walk: add code comments --- src/db/update/Walk.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 6ac12776e..fc4af265a 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -94,12 +94,16 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept { directory.ForEachChildSafe([&](Directory &child){ if (child.IsMount()) + /* mount points are always preserved */ return; if (DirectoryExists(storage, child) && child.IsPluginAvailable()) return; + /* the directory was deleted (or the plugin which + handles this "virtual" directory is unavailable) */ + editor.LockDeleteDirectory(&child); modified = true; From b5673b633347d1dc5d717c8869e00b6de6f80031 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 14:24:57 +0100 Subject: [PATCH 06/15] db/simple/Directory: add `pure` attribute --- src/db/plugins/simple/Directory.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx index ea14420d0..1479b578a 100644 --- a/src/db/plugins/simple/Directory.hxx +++ b/src/db/plugins/simple/Directory.hxx @@ -137,6 +137,7 @@ public: * (e.g. #DEVICE_PLAYLIST) and whether the underlying plugin * is available. */ + gcc_pure bool IsPluginAvailable() const noexcept; /** From bf97d13d0b571429b8ba4baf10d3b1a92b27a9ec Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 14:29:45 +0100 Subject: [PATCH 07/15] fs/Traits: add GetPathSuffix() --- src/fs/Traits.hxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx index b61de1833..32dcb4c60 100644 --- a/src/fs/Traits.hxx +++ b/src/fs/Traits.hxx @@ -96,6 +96,11 @@ struct PathTraitsFS { : nullptr; } + gcc_pure + static const_pointer GetPathSuffix(const_pointer path) noexcept { + return GetFilenameSuffix(GetBase(path)); + } + #ifdef _WIN32 gcc_pure gcc_nonnull_all static constexpr bool IsDrive(const_pointer p) noexcept { @@ -215,6 +220,11 @@ struct PathTraitsUTF8 { : nullptr; } + gcc_pure + static const_pointer GetPathSuffix(const_pointer path) noexcept { + return GetFilenameSuffix(GetBase(path)); + } + #ifdef _WIN32 gcc_pure gcc_nonnull_all static constexpr bool IsDrive(const_pointer p) noexcept { From bbfa6fe632747c7243b60ccf5d9652de793594a3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 14:24:44 +0100 Subject: [PATCH 08/15] db/simple: purge songs for unavailable decoder plugins on update --- NEWS | 3 ++- src/SongUpdate.cxx | 9 +++++++++ src/db/plugins/simple/Song.cxx | 8 ++++++++ src/db/plugins/simple/Song.hxx | 10 ++++++++++ src/db/update/Walk.cxx | 6 +++++- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 7700571c3..a654ef87a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ver 0.22.2 (not yet released) * database - - simple: purge virtual directories for unavailable plugins on update + - simple: purge songs and virtual directories for unavailable plugins + on update ver 0.22.1 (2020/10/17) * decoder diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx index c5701d9aa..2562bffb3 100644 --- a/src/SongUpdate.cxx +++ b/src/SongUpdate.cxx @@ -23,6 +23,7 @@ #include "db/plugins/simple/Directory.hxx" #include "storage/StorageInterface.hxx" #include "storage/FileInfo.hxx" +#include "decoder/DecoderList.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileInfo.hxx" #include "tag/Builder.hxx" @@ -40,6 +41,14 @@ #ifdef ENABLE_DATABASE +bool +Song::IsPluginAvailable() const noexcept +{ + const char *suffix = GetFilenameSuffix(); + return suffix != nullptr && + decoder_plugins_supports_suffix(suffix); +} + SongPtr Song::LoadFile(Storage &storage, const char *path_utf8, Directory &parent) { diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx index 3da13b9ea..eecf00277 100644 --- a/src/db/plugins/simple/Song.cxx +++ b/src/db/plugins/simple/Song.cxx @@ -34,6 +34,14 @@ Song::Song(DetachedSong &&other, Directory &_parent) noexcept { } +const char * +Song::GetFilenameSuffix() const noexcept +{ + return target.empty() + ? PathTraitsUTF8::GetFilenameSuffix(filename.c_str()) + : PathTraitsUTF8::GetPathSuffix(target.c_str()); +} + std::string Song::GetURI() const noexcept { diff --git a/src/db/plugins/simple/Song.hxx b/src/db/plugins/simple/Song.hxx index 04be80845..90334b031 100644 --- a/src/db/plugins/simple/Song.hxx +++ b/src/db/plugins/simple/Song.hxx @@ -108,6 +108,16 @@ struct Song { Song(DetachedSong &&other, Directory &_parent) noexcept; + gcc_pure + const char *GetFilenameSuffix() const noexcept; + + /** + * Checks whether the decoder plugin for this song is + * available. + */ + gcc_pure + bool IsPluginAvailable() const noexcept; + /** * allocate a new song structure with a local file name and attempt to * load its metadata. If all decoder plugin fail to read its meta diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index fc4af265a..3e0677e91 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -111,7 +111,11 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept directory.ForEachSongSafe([&](Song &song){ if (!directory_child_is_regular(storage, directory, - song.filename)) { + song.filename) || + !song.IsPluginAvailable()) { + /* the song file was deleted (or the decoder + plugin is unavailable) */ + editor.LockDeleteSong(directory, &song); modified = true; From 3562a3e51eba3c5f1f0b555ddc4fa6c47ec18a9a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 15:09:00 +0100 Subject: [PATCH 09/15] Main: save the state_file on shutdown This got lost in commit 5d597a3646cc (v0.21.19), but it was never noticed because the state_file_interval was way too short due to commit 3413d1bf23a, fixed recently by commit 27cc7b352d5 --- NEWS | 1 + src/Main.cxx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index a654ef87a..9654176d5 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ ver 0.22.2 (not yet released) * database - simple: purge songs and virtual directories for unavailable plugins on update +* state_file: save on shutdown ver 0.22.1 (2020/10/17) * decoder diff --git a/src/Main.cxx b/src/Main.cxx index c96dac4b7..8405b6fce 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -533,6 +533,9 @@ MainConfigured(const struct options &options, const ConfigData &raw_config) /* cleanup */ + if (instance.state_file) + instance.state_file->Write(); + instance.BeginShutdownUpdate(); ZeroconfDeinit(); From 1195eb266e2b1c5908d16a79c2d432359e7cd100 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 15:46:41 +0100 Subject: [PATCH 10/15] protocol/Ack: remove unused variable `ack_domain` --- meson.build | 1 - src/protocol/Ack.cxx | 23 ----------------------- src/protocol/Ack.hxx | 4 ---- test/meson.build | 1 - 4 files changed, 29 deletions(-) delete mode 100644 src/protocol/Ack.cxx diff --git a/meson.build b/meson.build index ecf2fe0e7..d70c9bf10 100644 --- a/meson.build +++ b/meson.build @@ -224,7 +224,6 @@ log_dep = declare_dependency( sources = [ version_cxx, 'src/Main.cxx', - 'src/protocol/Ack.cxx', 'src/protocol/ArgParser.cxx', 'src/protocol/Result.cxx', 'src/command/CommandError.cxx', diff --git a/src/protocol/Ack.cxx b/src/protocol/Ack.cxx deleted file mode 100644 index 5f4f42dd5..000000000 --- a/src/protocol/Ack.cxx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2003-2020 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 "Ack.hxx" -#include "util/Domain.hxx" - -const Domain ack_domain("ack"); diff --git a/src/protocol/Ack.hxx b/src/protocol/Ack.hxx index 2b479058b..ed5e851fa 100644 --- a/src/protocol/Ack.hxx +++ b/src/protocol/Ack.hxx @@ -25,8 +25,6 @@ #include #include -class Domain; - enum ack { ACK_ERROR_NOT_LIST = 1, ACK_ERROR_ARG = 2, @@ -43,8 +41,6 @@ enum ack { ACK_ERROR_EXIST = 56, }; -extern const Domain ack_domain; - class ProtocolError : public std::runtime_error { enum ack code; diff --git a/test/meson.build b/test/meson.build index a33b16d15..014899257 100644 --- a/test/meson.build +++ b/test/meson.build @@ -264,7 +264,6 @@ if enable_database executable( 'DumpDatabase', 'DumpDatabase.cxx', - '../src/protocol/Ack.cxx', '../src/db/Registry.cxx', '../src/db/Selection.cxx', '../src/db/PlaylistVector.cxx', From 6bc73a9ebe4256c2da305cf5ddbb60ec11b8625b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 15:48:23 +0100 Subject: [PATCH 11/15] util/FormatString: update API documentation --- src/util/FormatString.hxx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/util/FormatString.hxx b/src/util/FormatString.hxx index e1ecc8500..465ac7956 100644 --- a/src/util/FormatString.hxx +++ b/src/util/FormatString.hxx @@ -27,16 +27,14 @@ template class AllocatedString; /** - * Format into a newly allocated string. The caller frees the return - * value with delete[]. + * Format into an #AllocatedString. */ gcc_nonnull_all AllocatedString FormatStringV(const char *fmt, std::va_list args) noexcept; /** - * Format into a newly allocated string. The caller frees the return - * value with delete[]. + * Format into an #AllocatedString. */ gcc_nonnull(1) gcc_printf(1,2) AllocatedString From 5f61d440eb4a3a5647b1b04b5725abe7e020535a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 15:59:38 +0100 Subject: [PATCH 12/15] lib/yajl/Handle: un-inline the throwing code Reduces header dependencies. --- src/lib/yajl/Handle.cxx | 47 ++++++++++++++++++++++++++++++++++++++++ src/lib/yajl/Handle.hxx | 19 +++++----------- src/lib/yajl/meson.build | 1 + 3 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 src/lib/yajl/Handle.cxx diff --git a/src/lib/yajl/Handle.cxx b/src/lib/yajl/Handle.cxx new file mode 100644 index 000000000..fbfc8ac02 --- /dev/null +++ b/src/lib/yajl/Handle.cxx @@ -0,0 +1,47 @@ +/* + * Copyright 2018-2020 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Handle.hxx" +#include "util/RuntimeError.hxx" +#include "util/ScopeExit.hxx" + +namespace Yajl { + +void +Handle::ThrowError() +{ + unsigned char *str = yajl_get_error(handle, false, + nullptr, 0); + AtScopeExit(this, str) { + yajl_free_error(handle, str); + }; + throw FormatRuntimeError("Failed to parse JSON: %s", str); +} + +} // namespace Yajl diff --git a/src/lib/yajl/Handle.hxx b/src/lib/yajl/Handle.hxx index c2a148639..02fad8ea1 100644 --- a/src/lib/yajl/Handle.hxx +++ b/src/lib/yajl/Handle.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Max Kellermann + * Copyright 2018-2020 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,12 +30,8 @@ #ifndef YAJL_HANDLE_HXX #define YAJL_HANDLE_HXX -#include "util/RuntimeError.hxx" -#include "util/ScopeExit.hxx" - #include -#include #include namespace Yajl { @@ -77,15 +73,12 @@ public: private: void HandleStatus(yajl_status status) { - if (status == yajl_status_error) { - unsigned char *str = yajl_get_error(handle, false, - nullptr, 0); - AtScopeExit(this, str) { - yajl_free_error(handle, str); - }; - throw FormatRuntimeError("Failed to parse JSON: %s", str); - } + if (status == yajl_status_error) + ThrowError(); } + + [[noreturn]] + void ThrowError(); }; } // namespace Yajl diff --git a/src/lib/yajl/meson.build b/src/lib/yajl/meson.build index 91dc046fb..9e483669b 100644 --- a/src/lib/yajl/meson.build +++ b/src/lib/yajl/meson.build @@ -5,6 +5,7 @@ endif yajl = static_library( 'yajl', + 'Handle.cxx', 'ResponseParser.cxx', 'ParseInputStream.cxx', include_directories: inc, From 7b9295ff99f366fec9ba2ea0a12ed17b05b0af7c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 16:04:24 +0100 Subject: [PATCH 13/15] lib/yajl/Handle: strip newlines from error messages Closes https://github.com/MusicPlayerDaemon/MPD/issues/981 --- NEWS | 4 ++++ src/lib/yajl/Handle.cxx | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9654176d5..b8b2a003b 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ ver 0.22.2 (not yet released) * database - simple: purge songs and virtual directories for unavailable plugins on update +* input + - qobuz/tidal: fix protocol errors due to newlines in error messages +* playlist + - soundcloud: fix protocol errors due to newlines in error messages * state_file: save on shutdown ver 0.22.1 (2020/10/17) diff --git a/src/lib/yajl/Handle.cxx b/src/lib/yajl/Handle.cxx index fbfc8ac02..6a1741b10 100644 --- a/src/lib/yajl/Handle.cxx +++ b/src/lib/yajl/Handle.cxx @@ -30,6 +30,24 @@ #include "Handle.hxx" #include "util/RuntimeError.hxx" #include "util/ScopeExit.hxx" +#include "util/StringStrip.hxx" + +#include + +/** + * Strip whitespace at the beginning and end and replace newline + * characters which are illegal in the MPD protocol. + */ +static const char * +StripErrorMessage(char *s) noexcept +{ + s = Strip(s); + + while (auto newline = std::strchr(s, '\n')) + *newline = ';'; + + return s; +} namespace Yajl { @@ -41,7 +59,9 @@ Handle::ThrowError() AtScopeExit(this, str) { yajl_free_error(handle, str); }; - throw FormatRuntimeError("Failed to parse JSON: %s", str); + + throw FormatRuntimeError("Failed to parse JSON: %s", + StripErrorMessage((char *)str)); } } // namespace Yajl From 37710195ca581f3362e5db4ce3d5e4bdd2b1897e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 17:19:08 +0100 Subject: [PATCH 14/15] meson_options.txt: disable the "smbclient" plugin by default The bug https://bugzilla.samba.org/show_bug.cgi?id=11413 makes MPD crash after at most a minute of using the plugin. Since this bug is five years old already and it doesn't look like it will ever be fixed, all libsmbclient code in MPD is scheduled for removal. For now, the plugin is disabled by default so people are less likely to hit the crash bug. Closes https://github.com/MusicPlayerDaemon/MPD/issues/991 --- NEWS | 1 + doc/plugins.rst | 5 +++++ doc/user.rst | 2 +- meson_options.txt | 6 +++++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index b8b2a003b..162567f9f 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ver 0.22.2 (not yet released) on update * input - qobuz/tidal: fix protocol errors due to newlines in error messages + - smbclient: disable by default due to libsmbclient crash bug * playlist - soundcloud: fix protocol errors due to newlines in error messages * state_file: save on shutdown diff --git a/doc/plugins.rst b/doc/plugins.rst index e5056a7ab..107140942 100644 --- a/doc/plugins.rst +++ b/doc/plugins.rst @@ -71,6 +71,11 @@ Load music files from a SMB/CIFS server. It is used when :code:`music_directory` contains a ``smb://`` URI, for example :samp:`smb://myfileserver/Music`. +Note that :file:`libsmbclient` has a serious bug which causes MPD to +crash, and therefore this plugin is disabled by default and should not +be used until the bug is fixed: +https://bugzilla.samba.org/show_bug.cgi?id=11413 + nfs --- diff --git a/doc/user.rst b/doc/user.rst index 00ef9bdeb..db87ee7d0 100644 --- a/doc/user.rst +++ b/doc/user.rst @@ -86,7 +86,7 @@ For example, the following installs a fairly complete list of build dependencies libpulse-dev libshout3-dev \ libsndio-dev \ libmpdclient-dev \ - libnfs-dev libsmbclient-dev \ + libnfs-dev \ libupnp-dev \ libavahi-client-dev \ libsqlite3-dev \ diff --git a/meson_options.txt b/meson_options.txt index a1c5ec1ed..36214692d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -92,7 +92,11 @@ option('cdio_paranoia', type: 'feature', description: 'libcdio_paranoia input pl option('curl', type: 'feature', description: 'HTTP client using CURL') option('mms', type: 'feature', description: 'MMS protocol support using libmms') option('nfs', type: 'feature', description: 'NFS protocol support using libnfs') -option('smbclient', type: 'feature', description: 'SMB support using libsmbclient') + +# The "smbclient" plugin is disabled by default because libsmbclient +# has a serious bug which crashes MPD very quickly: +# https://bugzilla.samba.org/show_bug.cgi?id=11413 +option('smbclient', type: 'feature', value: 'disabled', description: 'SMB support using libsmbclient') # # Commercial services From dc432f3ffa9b1c8a60b9224c0ae00a40b8b819d1 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Oct 2020 17:25:33 +0100 Subject: [PATCH 15/15] release v0.22.2 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 162567f9f..a1b778d7e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ver 0.22.2 (not yet released) +ver 0.22.2 (2020/10/28) * database - simple: purge songs and virtual directories for unavailable plugins on update