Bläddra i källkod

Merge pull request #11680 from Shadowghost/secure-local-playlists

Secure local playlist path handling
Joshua M. Boniface 1 år sedan
förälder
incheckning
832e27a8fb
1 ändrade filer med 61 tillägg och 46 borttagningar
  1. 61 46
      MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs

+ 61 - 46
MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs

@@ -8,11 +8,13 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Logging;
 using PlaylistsNET.Content;
 
@@ -24,11 +26,16 @@ namespace MediaBrowser.Providers.Playlists
         IPreRefreshProvider,
         IHasItemChangeMonitor
     {
+        private readonly IFileSystem _fileSystem;
+        private readonly ILibraryManager _libraryManager;
         private readonly ILogger<PlaylistItemsProvider> _logger;
+        private readonly CollectionType[] _ignoredCollections = [CollectionType.livetv, CollectionType.boxsets, CollectionType.playlists];
 
-        public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger)
+        public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger, ILibraryManager libraryManager, IFileSystem fileSystem)
         {
             _logger = logger;
+            _libraryManager = libraryManager;
+            _fileSystem = fileSystem;
         }
 
         public string Name => "Playlist Reader";
@@ -59,109 +66,117 @@ namespace MediaBrowser.Providers.Playlists
 
         private IEnumerable<LinkedChild> GetItems(string path, string extension)
         {
+            var libraryRoots = _libraryManager.GetUserRootFolder().Children
+                                .OfType<CollectionFolder>()
+                                .Where(f => f.CollectionType.HasValue && !_ignoredCollections.Contains(f.CollectionType.Value))
+                                .SelectMany(f => f.PhysicalLocations)
+                                .Distinct(StringComparer.OrdinalIgnoreCase)
+                                .ToList();
+
             using (var stream = File.OpenRead(path))
             {
                 if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetWplItems(stream, path);
+                    return GetWplItems(stream, path, libraryRoots);
                 }
 
                 if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetZplItems(stream, path);
+                    return GetZplItems(stream, path, libraryRoots);
                 }
 
                 if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetM3uItems(stream, path);
+                    return GetM3uItems(stream, path, libraryRoots);
                 }
 
                 if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetM3u8Items(stream, path);
+                    return GetM3uItems(stream, path, libraryRoots);
                 }
 
                 if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetPlsItems(stream, path);
+                    return GetPlsItems(stream, path, libraryRoots);
                 }
             }
 
             return Enumerable.Empty<LinkedChild>();
         }
 
-        private IEnumerable<LinkedChild> GetPlsItems(Stream stream, string path)
+        private IEnumerable<LinkedChild> GetPlsItems(Stream stream, string playlistPath, List<string> libraryRoots)
         {
             var content = new PlsContent();
             var playlist = content.GetFromStream(stream);
 
-            return playlist.PlaylistEntries.Select(i => new LinkedChild
-            {
-                Path = GetPlaylistItemPath(i.Path, path),
-                Type = LinkedChildType.Manual
-            });
+            return playlist.PlaylistEntries
+                    .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
+                    .Where(i => i is not null);
         }
 
-        private IEnumerable<LinkedChild> GetM3u8Items(Stream stream, string path)
+        private IEnumerable<LinkedChild> GetM3uItems(Stream stream, string playlistPath, List<string> libraryRoots)
         {
             var content = new M3uContent();
             var playlist = content.GetFromStream(stream);
 
-            return playlist.PlaylistEntries.Select(i => new LinkedChild
-            {
-                Path = GetPlaylistItemPath(i.Path, path),
-                Type = LinkedChildType.Manual
-            });
+            return playlist.PlaylistEntries
+                    .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
+                    .Where(i => i is not null);
         }
 
-        private IEnumerable<LinkedChild> GetM3uItems(Stream stream, string path)
+        private IEnumerable<LinkedChild> GetZplItems(Stream stream, string playlistPath, List<string> libraryRoots)
         {
-            var content = new M3uContent();
+            var content = new ZplContent();
             var playlist = content.GetFromStream(stream);
 
-            return playlist.PlaylistEntries.Select(i => new LinkedChild
-            {
-                Path = GetPlaylistItemPath(i.Path, path),
-                Type = LinkedChildType.Manual
-            });
+            return playlist.PlaylistEntries
+                    .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
+                    .Where(i => i is not null);
         }
 
-        private IEnumerable<LinkedChild> GetZplItems(Stream stream, string path)
+        private IEnumerable<LinkedChild> GetWplItems(Stream stream, string playlistPath, List<string> libraryRoots)
         {
-            var content = new ZplContent();
+            var content = new WplContent();
             var playlist = content.GetFromStream(stream);
 
-            return playlist.PlaylistEntries.Select(i => new LinkedChild
-            {
-                Path = GetPlaylistItemPath(i.Path, path),
-                Type = LinkedChildType.Manual
-            });
+            return playlist.PlaylistEntries
+                    .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
+                    .Where(i => i is not null);
         }
 
-        private IEnumerable<LinkedChild> GetWplItems(Stream stream, string path)
+        private LinkedChild GetLinkedChild(string itemPath, string playlistPath, List<string> libraryRoots)
         {
-            var content = new WplContent();
-            var playlist = content.GetFromStream(stream);
-
-            return playlist.PlaylistEntries.Select(i => new LinkedChild
+            if (TryGetPlaylistItemPath(itemPath, playlistPath, libraryRoots, out var parsedPath))
             {
-                Path = GetPlaylistItemPath(i.Path, path),
-                Type = LinkedChildType.Manual
-            });
+                return new LinkedChild
+                {
+                    Path = parsedPath,
+                    Type = LinkedChildType.Manual
+                };
+            }
+
+            return null;
         }
 
-        private string GetPlaylistItemPath(string itemPath, string containingPlaylistFolder)
+        private bool TryGetPlaylistItemPath(string itemPath, string playlistPath, List<string> libraryPaths, out string path)
         {
-            if (!File.Exists(itemPath))
+            path = null;
+            string pathToCheck = _fileSystem.MakeAbsolutePath(Path.GetDirectoryName(playlistPath), itemPath);
+            if (!File.Exists(pathToCheck))
             {
-                var path = Path.Combine(Path.GetDirectoryName(containingPlaylistFolder), itemPath);
-                if (File.Exists(path))
+                return false;
+            }
+
+            foreach (var libraryPath in libraryPaths)
+            {
+                if (pathToCheck.StartsWith(libraryPath, StringComparison.OrdinalIgnoreCase))
                 {
-                    return path;
+                    path = pathToCheck;
+                    return true;
                 }
             }
 
-            return itemPath;
+            return false;
         }
 
         public bool HasChanged(BaseItem item, IDirectoryService directoryService)