PageRenderTime 229ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/PlugIns/MPExtended.PlugIns.MAS.MPShares/ShareLibrary.cs

https://github.com/MPExtended/MPExtended
C# | 326 lines | 271 code | 38 blank | 17 comment | 39 complexity | d5c774cc6c205f30ad6c306bf21a1826 MD5 | raw file
  1. #region Copyright (C) 2011-2013 MPExtended
  2. // Copyright (C) 2011-2013 MPExtended Developers, http://www.mpextended.com/
  3. //
  4. // MPExtended is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 2 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // MPExtended is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with MPExtended. If not, see <http://www.gnu.org/licenses/>.
  16. #endregion
  17. using System;
  18. using System.Collections;
  19. using System.Collections.Generic;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Text;
  23. using System.Xml.Linq;
  24. using MPExtended.Libraries.Service;
  25. using MPExtended.Libraries.Service.Util;
  26. using MPExtended.Services.Common.Interfaces;
  27. using MPExtended.Services.MediaAccessService.Interfaces;
  28. using MPExtended.Services.MediaAccessService.Interfaces.FileSystem;
  29. namespace MPExtended.PlugIns.MAS.MPShares
  30. {
  31. public abstract class ShareLibrary : IFileSystemLibrary
  32. {
  33. private IPluginData data;
  34. private Dictionary<string, string> configuration;
  35. private string[] sections;
  36. private List<Share> shares = null;
  37. public bool Supported { get; set; }
  38. public ShareLibrary(IPluginData data, string[] sections)
  39. {
  40. this.data = data;
  41. this.configuration = data.GetConfiguration("MP Shares");
  42. this.sections = sections;
  43. Supported = Mediaportal.HasValidConfigFile();
  44. if (Supported)
  45. {
  46. ReadConfiguration();
  47. ConfigurationChangeListener.ConfigurationChanged += ReadConfiguration;
  48. ConfigurationChangeListener.Enable();
  49. }
  50. }
  51. private void ReadConfiguration()
  52. {
  53. var localsharelist = new List<Share>();
  54. foreach (string section in sections)
  55. {
  56. IEnumerable<KeyValuePair<string, string>> list = Mediaportal.ReadSectionFromConfigFile(section);
  57. if (!list.Any())
  58. {
  59. Log.Warn("MPShares: Failed to read section {0} from MediaPortal configuration file, aborting configuration reading", section);
  60. return;
  61. }
  62. string[] extensions = list.Where(x => x.Key == "extensions").Select(x => x.Value).First().Split(',');
  63. int count = list.Where(x => x.Key.StartsWith("sharename")).Count();
  64. for (int i = 0; i < count; i++)
  65. {
  66. if (list.Where(x => x.Key == "sharetype" + i).Select(x => x.Value).First() == "yes")
  67. {
  68. continue;
  69. }
  70. string path = list.Where(x => x.Key == "sharepath" + i).Select(x => x.Value).First();
  71. localsharelist.Add(new Share()
  72. {
  73. Name = list.Where(x => x.Key == "sharename" + i).Select(x => x.Value).First(),
  74. Path = path,
  75. Extensions = extensions.ToList(),
  76. });
  77. }
  78. }
  79. // make shares unique
  80. shares = localsharelist.GroupBy(x => x.Path, (path, gshares) => new Share()
  81. {
  82. Name = gshares.First().Name,
  83. Path = path,
  84. Extensions = gshares.SelectMany(x => x.Extensions).ToList()
  85. }).ToList();
  86. int shareNr = 0;
  87. foreach (Share share in shares)
  88. {
  89. share.Id = "s" + (shareNr++);
  90. }
  91. }
  92. public ShareLibrary(IPluginData data, string section)
  93. : this(data, new string[] { section })
  94. {
  95. }
  96. public IEnumerable<WebDriveBasic> GetDriveListing()
  97. {
  98. return shares.Select(x => x.ToWebDriveBasic());
  99. }
  100. public IEnumerable<WebFolderBasic> GetFoldersListing(string id)
  101. {
  102. string path = GetPath(id);
  103. if (!String.IsNullOrEmpty(path) && Directory.Exists(path))
  104. {
  105. return new DirectoryInfo(path).GetDirectories().Select(dir => ConvertDirectoryInfoToFolderBasic(dir));
  106. }
  107. return new List<WebFolderBasic>();
  108. }
  109. public IEnumerable<WebFileBasic> GetFilesListing(string id)
  110. {
  111. string path = GetPath(id);
  112. if (!String.IsNullOrEmpty(path) && Directory.Exists(path))
  113. {
  114. Share share = GetShare(id);
  115. return new DirectoryInfo(path)
  116. .GetFiles()
  117. .Where(file => share.Extensions.Any(x => x.Equals(file.Extension, StringComparison.CurrentCultureIgnoreCase)))
  118. .Select(file => ConvertFileInfoToFileBasic(file, share));
  119. }
  120. return new List<WebFileBasic>();
  121. }
  122. public WebDriveBasic GetDriveBasic(string id)
  123. {
  124. string path = GetPath(id);
  125. return shares.First(x => x.Path == path).ToWebDriveBasic();
  126. }
  127. public WebFolderBasic GetFolderBasic(string id)
  128. {
  129. string path = GetPath(id);
  130. if (Directory.Exists(path))
  131. {
  132. return ConvertDirectoryInfoToFolderBasic(new DirectoryInfo(path));
  133. }
  134. return null;
  135. }
  136. public WebFileBasic GetFileBasic(string id)
  137. {
  138. string path = GetPath(id);
  139. if (!File.Exists(path))
  140. return null;
  141. return ConvertFileInfoToFileBasic(new FileInfo(PathUtil.StripFileProtocolPrefix(path)));
  142. }
  143. public WebFileInfo GetFileInfo(string path)
  144. {
  145. return new WebFileInfo(PathUtil.StripFileProtocolPrefix(path));
  146. }
  147. public Stream GetFile(string path)
  148. {
  149. return new FileStream(path, FileMode.Open, FileAccess.Read);
  150. }
  151. public IEnumerable<WebSearchResult> Search(string text)
  152. {
  153. return new List<WebSearchResult>();
  154. }
  155. public WebDictionary<string> GetExternalMediaInfo(WebMediaType type, string id)
  156. {
  157. string path = GetPath(id);
  158. return new WebDictionary<string>()
  159. {
  160. { "Type", File.Exists(path) ? "file" : "folder" },
  161. { "Path", path },
  162. { "Extensions", String.Join("|", GetShare(id).Extensions) }
  163. };
  164. }
  165. private WebFileBasic ConvertFileInfoToFileBasic(FileInfo file, Share share = null)
  166. {
  167. return new WebFileBasic()
  168. {
  169. Title = file.Name,
  170. Path = new List<string>() { file.FullName },
  171. DateAdded = file.CreationTime,
  172. Id = PathToIdentifier(file, share),
  173. LastAccessTime = file.LastAccessTime,
  174. LastModifiedTime = file.LastWriteTime,
  175. Size = file.Length
  176. };
  177. }
  178. private WebFolderBasic ConvertDirectoryInfoToFolderBasic(DirectoryInfo dir, Share share = null)
  179. {
  180. return new WebFolderBasic()
  181. {
  182. Title = dir.Name,
  183. Path = new List<string>() { dir.FullName },
  184. DateAdded = dir.CreationTime,
  185. Id = PathToIdentifier(dir, share),
  186. LastAccessTime = dir.LastAccessTime,
  187. LastModifiedTime = dir.LastWriteTime
  188. };
  189. }
  190. private string GetPath(string id)
  191. {
  192. if (id.StartsWith("s"))
  193. {
  194. return GetShare(id).Path;
  195. }
  196. else if (id.StartsWith("d") || id.StartsWith("f"))
  197. {
  198. Share share = GetShare(id);
  199. string reldir = DecodeFrom64(id.Substring(id.IndexOf("_") + 1));
  200. if (!String.IsNullOrEmpty(reldir) && share != null)
  201. {
  202. string path = Path.GetFullPath(Path.Combine(share.Path, reldir));
  203. // it is possible that someone tricks us into looking outside of the shareroot by a /../ path
  204. if (Security.IsInShare(path, share))
  205. {
  206. return path;
  207. }
  208. }
  209. return null;
  210. }
  211. else
  212. {
  213. // malformed identifier, do not decode it as a path here because that would give access to arbitrary files
  214. return null;
  215. }
  216. }
  217. private Share GetShare(string id)
  218. {
  219. if (id.StartsWith("s"))
  220. {
  221. return shares.Where(x => x.Id == id).First();
  222. }
  223. else if (id.StartsWith("d") || id.StartsWith("f"))
  224. {
  225. string sid = id.Substring(1, id.IndexOf("_") - 1);
  226. if (!String.IsNullOrEmpty(sid))
  227. {
  228. return shares.FirstOrDefault(x => x.Id == sid);
  229. }
  230. return null;
  231. }
  232. else
  233. {
  234. return null;
  235. }
  236. }
  237. private string PathToIdentifier(FileInfo info, Share useShare = null)
  238. {
  239. if(useShare == null)
  240. {
  241. return PathToIdentifier(info.FullName, useShare);
  242. }
  243. return "f" + useShare.Id + "_" + EncodeTo64(info.FullName.Substring(useShare.Path.Length + 1));
  244. }
  245. private string PathToIdentifier(DirectoryInfo info, Share useShare = null)
  246. {
  247. if (useShare == null)
  248. {
  249. return PathToIdentifier(info.FullName, useShare);
  250. }
  251. return "d" + useShare.Id + "_" + EncodeTo64(info.FullName.Substring(useShare.Path.Length + 1));
  252. }
  253. private string PathToIdentifier(string path, Share useShare = null)
  254. {
  255. foreach (Share share in shares)
  256. {
  257. if (path.StartsWith(share.Path + @"\"))
  258. {
  259. string type = File.Exists(path) ? "f" : "d";
  260. return type + share.Id + "_" + EncodeTo64(path.Substring(share.Path.Length + 1));
  261. }
  262. else if (path == share.Path)
  263. {
  264. return "s" + share.Id;
  265. }
  266. }
  267. return String.Empty;
  268. }
  269. private string EncodeTo64(string toEncode)
  270. {
  271. byte[] toEncodeAsBytes = Encoding.UTF8.GetBytes(toEncode);
  272. return Convert.ToBase64String(toEncodeAsBytes);
  273. }
  274. private string DecodeFrom64(string encodedData)
  275. {
  276. try
  277. {
  278. byte[] encodedDataAsBytes = Convert.FromBase64String(encodedData);
  279. return Encoding.UTF8.GetString(encodedDataAsBytes);
  280. }
  281. catch (FormatException)
  282. {
  283. Log.Warn("MPShares: Invalid base64 input {0}", encodedData);
  284. return String.Empty;
  285. }
  286. }
  287. }
  288. }