From ecaa51e322095dce9ccc91984c605ffd8dd994ef Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Tue, 27 Oct 2020 19:00:46 +0100
Subject: [PATCH] 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);