EnvironmentService.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using MediaBrowser.Common.Net;
  6. using MediaBrowser.Controller.Configuration;
  7. using MediaBrowser.Controller.Net;
  8. using MediaBrowser.Model.IO;
  9. using MediaBrowser.Model.Net;
  10. using MediaBrowser.Model.Services;
  11. using Microsoft.Extensions.Logging;
  12. namespace MediaBrowser.Api
  13. {
  14. /// <summary>
  15. /// Class GetDirectoryContents
  16. /// </summary>
  17. [Route("/Environment/DirectoryContents", "GET", Summary = "Gets the contents of a given directory in the file system")]
  18. public class GetDirectoryContents : IReturn<List<FileSystemEntryInfo>>
  19. {
  20. /// <summary>
  21. /// Gets or sets the path.
  22. /// </summary>
  23. /// <value>The path.</value>
  24. [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
  25. public string Path { get; set; }
  26. /// <summary>
  27. /// Gets or sets a value indicating whether [include files].
  28. /// </summary>
  29. /// <value><c>true</c> if [include files]; otherwise, <c>false</c>.</value>
  30. [ApiMember(Name = "IncludeFiles", Description = "An optional filter to include or exclude files from the results. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  31. public bool IncludeFiles { get; set; }
  32. /// <summary>
  33. /// Gets or sets a value indicating whether [include directories].
  34. /// </summary>
  35. /// <value><c>true</c> if [include directories]; otherwise, <c>false</c>.</value>
  36. [ApiMember(Name = "IncludeDirectories", Description = "An optional filter to include or exclude folders from the results. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  37. public bool IncludeDirectories { get; set; }
  38. }
  39. [Route("/Environment/ValidatePath", "POST", Summary = "Gets the contents of a given directory in the file system")]
  40. public class ValidatePath
  41. {
  42. /// <summary>
  43. /// Gets or sets the path.
  44. /// </summary>
  45. /// <value>The path.</value>
  46. [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
  47. public string Path { get; set; }
  48. public bool ValidateWriteable { get; set; }
  49. public bool? IsFile { get; set; }
  50. }
  51. [Obsolete]
  52. [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")]
  53. public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>>
  54. {
  55. /// <summary>
  56. /// Gets or sets the path.
  57. /// </summary>
  58. /// <value>The path.</value>
  59. [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
  60. public string Path { get; set; }
  61. }
  62. /// <summary>
  63. /// Class GetDrives
  64. /// </summary>
  65. [Route("/Environment/Drives", "GET", Summary = "Gets available drives from the server's file system")]
  66. public class GetDrives : IReturn<List<FileSystemEntryInfo>>
  67. {
  68. }
  69. /// <summary>
  70. /// Class GetNetworkComputers
  71. /// </summary>
  72. [Route("/Environment/NetworkDevices", "GET", Summary = "Gets a list of devices on the network")]
  73. public class GetNetworkDevices : IReturn<List<FileSystemEntryInfo>>
  74. {
  75. }
  76. [Route("/Environment/ParentPath", "GET", Summary = "Gets the parent path of a given path")]
  77. public class GetParentPath : IReturn<string>
  78. {
  79. /// <summary>
  80. /// Gets or sets the path.
  81. /// </summary>
  82. /// <value>The path.</value>
  83. [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
  84. public string Path { get; set; }
  85. }
  86. public class DefaultDirectoryBrowserInfo
  87. {
  88. public string Path { get; set; }
  89. }
  90. [Route("/Environment/DefaultDirectoryBrowser", "GET", Summary = "Gets the parent path of a given path")]
  91. public class GetDefaultDirectoryBrowser : IReturn<DefaultDirectoryBrowserInfo>
  92. {
  93. }
  94. /// <summary>
  95. /// Class EnvironmentService
  96. /// </summary>
  97. [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
  98. public class EnvironmentService : BaseApiService
  99. {
  100. private const char UncSeparator = '\\';
  101. private const string UncSeparatorString = "\\";
  102. /// <summary>
  103. /// The _network manager
  104. /// </summary>
  105. private readonly INetworkManager _networkManager;
  106. private readonly IFileSystem _fileSystem;
  107. /// <summary>
  108. /// Initializes a new instance of the <see cref="EnvironmentService" /> class.
  109. /// </summary>
  110. /// <param name="networkManager">The network manager.</param>
  111. public EnvironmentService(
  112. ILogger<EnvironmentService> logger,
  113. IServerConfigurationManager serverConfigurationManager,
  114. IHttpResultFactory httpResultFactory,
  115. INetworkManager networkManager,
  116. IFileSystem fileSystem)
  117. : base(logger, serverConfigurationManager, httpResultFactory)
  118. {
  119. _networkManager = networkManager;
  120. _fileSystem = fileSystem;
  121. }
  122. public void Post(ValidatePath request)
  123. {
  124. if (request.IsFile.HasValue)
  125. {
  126. if (request.IsFile.Value)
  127. {
  128. if (!File.Exists(request.Path))
  129. {
  130. throw new FileNotFoundException("File not found", request.Path);
  131. }
  132. }
  133. else
  134. {
  135. if (!Directory.Exists(request.Path))
  136. {
  137. throw new FileNotFoundException("File not found", request.Path);
  138. }
  139. }
  140. }
  141. else
  142. {
  143. if (!File.Exists(request.Path) && !Directory.Exists(request.Path))
  144. {
  145. throw new FileNotFoundException("Path not found", request.Path);
  146. }
  147. if (request.ValidateWriteable)
  148. {
  149. EnsureWriteAccess(request.Path);
  150. }
  151. }
  152. }
  153. protected void EnsureWriteAccess(string path)
  154. {
  155. var file = Path.Combine(path, Guid.NewGuid().ToString());
  156. File.WriteAllText(file, string.Empty);
  157. _fileSystem.DeleteFile(file);
  158. }
  159. public object Get(GetDefaultDirectoryBrowser request) =>
  160. ToOptimizedResult(new DefaultDirectoryBrowserInfo {Path = null});
  161. /// <summary>
  162. /// Gets the specified request.
  163. /// </summary>
  164. /// <param name="request">The request.</param>
  165. /// <returns>System.Object.</returns>
  166. public object Get(GetDirectoryContents request)
  167. {
  168. var path = request.Path;
  169. if (string.IsNullOrEmpty(path))
  170. {
  171. throw new ArgumentNullException(nameof(Path));
  172. }
  173. var networkPrefix = UncSeparatorString + UncSeparatorString;
  174. if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase)
  175. && path.LastIndexOf(UncSeparator) == 1)
  176. {
  177. return ToOptimizedResult(Array.Empty<FileSystemEntryInfo>());
  178. }
  179. return ToOptimizedResult(GetFileSystemEntries(request).ToList());
  180. }
  181. [Obsolete]
  182. public object Get(GetNetworkShares request)
  183. => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>());
  184. /// <summary>
  185. /// Gets the specified request.
  186. /// </summary>
  187. /// <param name="request">The request.</param>
  188. /// <returns>System.Object.</returns>
  189. public object Get(GetDrives request)
  190. {
  191. var result = GetDrives().ToList();
  192. return ToOptimizedResult(result);
  193. }
  194. /// <summary>
  195. /// Gets the list that is returned when an empty path is supplied
  196. /// </summary>
  197. /// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
  198. private IEnumerable<FileSystemEntryInfo> GetDrives()
  199. {
  200. return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo
  201. {
  202. Name = d.Name,
  203. Path = d.FullName,
  204. Type = FileSystemEntryType.Directory
  205. });
  206. }
  207. /// <summary>
  208. /// Gets the specified request.
  209. /// </summary>
  210. /// <param name="request">The request.</param>
  211. /// <returns>System.Object.</returns>
  212. public object Get(GetNetworkDevices request)
  213. => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>());
  214. /// <summary>
  215. /// Gets the file system entries.
  216. /// </summary>
  217. /// <param name="request">The request.</param>
  218. /// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
  219. private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
  220. {
  221. var entries = _fileSystem.GetFileSystemEntries(request.Path).OrderBy(i => i.FullName).Where(i =>
  222. {
  223. var isDirectory = i.IsDirectory;
  224. if (!request.IncludeFiles && !isDirectory)
  225. {
  226. return false;
  227. }
  228. if (!request.IncludeDirectories && isDirectory)
  229. {
  230. return false;
  231. }
  232. return true;
  233. });
  234. return entries.Select(f => new FileSystemEntryInfo
  235. {
  236. Name = f.Name,
  237. Path = f.FullName,
  238. Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
  239. });
  240. }
  241. public object Get(GetParentPath request)
  242. {
  243. var parent = Path.GetDirectoryName(request.Path);
  244. if (string.IsNullOrEmpty(parent))
  245. {
  246. // Check if unc share
  247. var index = request.Path.LastIndexOf(UncSeparator);
  248. if (index != -1 && request.Path.IndexOf(UncSeparator) == 0)
  249. {
  250. parent = request.Path.Substring(0, index);
  251. if (string.IsNullOrWhiteSpace(parent.TrimStart(UncSeparator)))
  252. {
  253. parent = null;
  254. }
  255. }
  256. }
  257. return parent;
  258. }
  259. }
  260. }