InventoryArchiveUtils.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Text;
  31. using log4net;
  32. using OpenMetaverse;
  33. using OpenSim.Framework;
  34. using OpenSim.Services.Interfaces;
  35. namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
  36. {
  37. /// <summary>
  38. /// Utility methods for inventory archiving
  39. /// </summary>
  40. public static class InventoryArchiveUtils
  41. {
  42. // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  43. // Character used for escaping the path delimter ("\/") and itself ("\\") in human escaped strings
  44. public static readonly char ESCAPE_CHARACTER = '\\';
  45. // The character used to separate inventory path components (different folders and items)
  46. public static readonly char PATH_DELIMITER = '/';
  47. /// <summary>
  48. /// Find a folder given a PATH_DELIMITER delimited path starting from a user's root folder
  49. /// </summary>
  50. /// <remarks>
  51. /// This method does not handle paths that contain multiple delimitors
  52. ///
  53. /// FIXME: We have no way of distinguishing folders with the same path
  54. ///
  55. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  56. /// </remarks>
  57. /// <param name="inventoryService">
  58. /// Inventory service to query
  59. /// </param>
  60. /// <param name="userId">
  61. /// User id to search
  62. /// </param>
  63. /// <param name="path">
  64. /// The path to the required folder.
  65. /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned.
  66. /// </param>
  67. /// <returns>The folder found. Please note that if there are multiple folders with the same name then an
  68. /// unspecified one will be returned. If no such folder eixsts then null is returned</returns>
  69. public static InventoryFolderBase FindFolderByPath(
  70. IInventoryService inventoryService, UUID userId, string path)
  71. {
  72. List<InventoryFolderBase> folders = FindFoldersByPath(inventoryService, userId, path);
  73. if (folders.Count == 0)
  74. return null;
  75. else
  76. return folders[0];
  77. }
  78. /// <summary>
  79. /// Find a folder given a PATH_DELIMITER delimited path starting from a given folder
  80. /// </summary>
  81. /// <remarks>
  82. /// This method does not handle paths that contain multiple delimitors
  83. ///
  84. /// FIXME: We have no way of distinguishing folders with the same path
  85. ///
  86. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  87. /// </remarks>
  88. /// <param name="inventoryService">
  89. /// Inventory service to query
  90. /// </param>
  91. /// <param name="startFolder">
  92. /// The folder from which the path starts
  93. /// </param>
  94. /// <param name="path">
  95. /// The path to the required folder.
  96. /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned.
  97. /// </param>
  98. /// <returns>The folder found. Please note that if there are multiple folders with the same name then an
  99. /// unspecified one will be returned. If no such folder eixsts then null is returned</returns>
  100. public static InventoryFolderBase FindFolderByPath(
  101. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  102. {
  103. if (null == startFolder)
  104. return null;
  105. List<InventoryFolderBase> folders = FindFoldersByPath(inventoryService, startFolder, path);
  106. if (folders.Count == 0)
  107. return null;
  108. else
  109. return folders[0];
  110. }
  111. /// <summary>
  112. /// Find a set of folders given a PATH_DELIMITER delimited path starting from a user's root folder
  113. /// </summary>
  114. /// <remarks>
  115. /// This method does not handle paths that contain multiple delimitors
  116. ///
  117. /// FIXME: We have no way of distinguishing folders with the same path
  118. ///
  119. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  120. /// </remarks>
  121. /// <param name="inventoryService">
  122. /// Inventory service to query
  123. /// </param>
  124. /// <param name="userId">
  125. /// User id to search
  126. /// </param>
  127. /// <param name="path">
  128. /// The path to the required folder.
  129. /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned.
  130. /// </param>
  131. /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns>
  132. public static List<InventoryFolderBase> FindFoldersByPath(
  133. IInventoryService inventoryService, UUID userId, string path)
  134. {
  135. InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
  136. if (null == rootFolder)
  137. return new List<InventoryFolderBase>();
  138. return FindFoldersByPath(inventoryService, rootFolder, path);
  139. }
  140. /// <summary>
  141. /// Find a set of folders given a PATH_DELIMITER delimited path starting from this folder
  142. /// </summary>
  143. /// <remarks>
  144. /// This method does not handle paths that contain multiple delimitors
  145. ///
  146. /// FIXME: We have no way of distinguishing folders with the same path.
  147. ///
  148. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  149. /// </remarks>
  150. /// <param name="inventoryService">
  151. /// Inventory service to query
  152. /// </param>
  153. /// <param name="startFolder">
  154. /// The folder from which the path starts
  155. /// </param>
  156. /// <param name="path">
  157. /// The path to the required folder.
  158. /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned.
  159. /// </param>
  160. /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns>
  161. public static List<InventoryFolderBase> FindFoldersByPath(
  162. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  163. {
  164. List<InventoryFolderBase> foundFolders = new List<InventoryFolderBase>();
  165. if (path == string.Empty)
  166. {
  167. foundFolders.Add(startFolder);
  168. return foundFolders;
  169. }
  170. path = path.Trim();
  171. if (path == PATH_DELIMITER.ToString())
  172. {
  173. foundFolders.Add(startFolder);
  174. return foundFolders;
  175. }
  176. // If the path isn't just / then trim any starting extraneous slashes
  177. path = path.TrimStart(new char[] { PATH_DELIMITER });
  178. // m_log.DebugFormat("[INVENTORY ARCHIVE UTILS]: Adjusted path in FindFolderByPath() is [{0}]", path);
  179. string[] components = SplitEscapedPath(path);
  180. components[0] = UnescapePath(components[0]);
  181. //string[] components = path.Split(new string[] { PATH_DELIMITER.ToString() }, 2, StringSplitOptions.None);
  182. InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID);
  183. // m_log.DebugFormat(
  184. // "Found {0} folders in {1} for {2}", contents.Folders.Count, startFolder.Name, startFolder.Owner);
  185. foreach (InventoryFolderBase folder in contents.Folders)
  186. {
  187. if (folder.Name == components[0])
  188. {
  189. if (components.Length > 1)
  190. foundFolders.AddRange(FindFoldersByPath(inventoryService, folder, components[1]));
  191. else
  192. foundFolders.Add(folder);
  193. }
  194. }
  195. return foundFolders;
  196. }
  197. /// <summary>
  198. /// Find an item given a PATH_DELIMITOR delimited path starting from the user's root folder.
  199. /// </summary>
  200. /// <remarks>
  201. /// This method does not handle paths that contain multiple delimitors
  202. ///
  203. /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some
  204. /// XPath like expression
  205. ///
  206. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  207. /// </remarks>
  208. ///
  209. /// <param name="inventoryService">
  210. /// Inventory service to query
  211. /// </param>
  212. /// <param name="userId">
  213. /// The user to search
  214. /// </param>
  215. /// <param name="path">
  216. /// The path to the required item.
  217. /// </param>
  218. /// <returns>null if the item is not found</returns>
  219. public static InventoryItemBase FindItemByPath(
  220. IInventoryService inventoryService, UUID userId, string path)
  221. {
  222. InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
  223. if (null == rootFolder)
  224. return null;
  225. return FindItemByPath(inventoryService, rootFolder, path);
  226. }
  227. /// <summary>
  228. /// Find an item given a PATH_DELIMITOR delimited path starting from this folder.
  229. /// </summary>
  230. /// <remarks>
  231. /// This method does not handle paths that contain multiple delimiters
  232. ///
  233. /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some
  234. /// XPath like expression
  235. ///
  236. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  237. /// </remarks>
  238. ///
  239. /// <param name="inventoryService">Inventory service to query</param>
  240. /// <param name="startFolder">The folder from which the path starts</param>
  241. /// <param name="path">The path to the required item.</param>
  242. /// <returns>null if the item is not found</returns>
  243. public static InventoryItemBase FindItemByPath(
  244. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  245. {
  246. List<InventoryItemBase> foundItems = FindItemsByPath(inventoryService, startFolder, path);
  247. if (foundItems.Count != 0)
  248. return foundItems[0];
  249. else
  250. return null;
  251. }
  252. public static List<InventoryItemBase> FindItemsByPath(
  253. IInventoryService inventoryService, UUID userId, string path)
  254. {
  255. InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
  256. if (null == rootFolder)
  257. return new List<InventoryItemBase>();
  258. return FindItemsByPath(inventoryService, rootFolder, path);
  259. }
  260. /// <summary>
  261. /// Find items that match a given PATH_DELIMITOR delimited path starting from this folder.
  262. /// </summary>
  263. /// <remarks>
  264. /// This method does not handle paths that contain multiple delimiters
  265. ///
  266. /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some
  267. /// XPath like expression
  268. ///
  269. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  270. /// </remarks>
  271. ///
  272. /// <param name="inventoryService">Inventory service to query</param>
  273. /// <param name="startFolder">The folder from which the path starts</param>
  274. /// <param name="path">The path to the required item.</param>
  275. /// <returns>The items that were found with this path. An empty list if no items were found.</returns>
  276. public static List<InventoryItemBase> FindItemsByPath(
  277. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  278. {
  279. List<InventoryItemBase> foundItems = new List<InventoryItemBase>();
  280. // If the path isn't just / then trim any starting extraneous slashes
  281. path = path.TrimStart(new char[] { PATH_DELIMITER });
  282. string[] components = SplitEscapedPath(path);
  283. components[0] = UnescapePath(components[0]);
  284. //string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None);
  285. if (components.Length == 1)
  286. {
  287. // m_log.DebugFormat(
  288. // "FOUND SINGLE COMPONENT [{0}]. Looking for this in [{1}] {2}",
  289. // components[0], startFolder.Name, startFolder.ID);
  290. List<InventoryItemBase> items = inventoryService.GetFolderItems(startFolder.Owner, startFolder.ID);
  291. // m_log.DebugFormat("[INVENTORY ARCHIVE UTILS]: Found {0} items in FindItemByPath()", items.Count);
  292. foreach (InventoryItemBase item in items)
  293. {
  294. // m_log.DebugFormat("[INVENTORY ARCHIVE UTILS]: Inspecting item {0} {1}", item.Name, item.ID);
  295. if (item.Name == components[0])
  296. foundItems.Add(item);
  297. }
  298. }
  299. else
  300. {
  301. // m_log.DebugFormat("FOUND COMPONENTS [{0}] and [{1}]", components[0], components[1]);
  302. InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID);
  303. foreach (InventoryFolderBase folder in contents.Folders)
  304. {
  305. if (folder.Name == components[0])
  306. foundItems.AddRange(FindItemsByPath(inventoryService, folder, components[1]));
  307. }
  308. }
  309. return foundItems;
  310. }
  311. /// <summary>
  312. /// Split a human escaped path into two components if it contains an unescaped path delimiter, or one component
  313. /// if no delimiter is present
  314. /// </summary>
  315. /// <param name="path"></param>
  316. /// <returns>
  317. /// The split path. We leave the components in their originally unescaped state (though we remove the delimiter
  318. /// which originally split them if applicable).
  319. /// </returns>
  320. public static string[] SplitEscapedPath(string path)
  321. {
  322. // m_log.DebugFormat("SPLITTING PATH {0}", path);
  323. bool singleEscapeChar = false;
  324. for (int i = 0; i < path.Length; i++)
  325. {
  326. if (path[i] == ESCAPE_CHARACTER && !singleEscapeChar)
  327. {
  328. singleEscapeChar = true;
  329. }
  330. else
  331. {
  332. if (PATH_DELIMITER == path[i] && !singleEscapeChar)
  333. return new string[2] { path.Remove(i), path.Substring(i + 1) };
  334. else
  335. singleEscapeChar = false;
  336. }
  337. }
  338. // We didn't find a delimiter
  339. return new string[1] { path };
  340. }
  341. /// <summary>
  342. /// Unescapes a human escaped path. This means that "\\" goes to "\", and "\/" goes to "/"
  343. /// </summary>
  344. /// <param name="path"></param>
  345. /// <returns></returns>
  346. public static string UnescapePath(string path)
  347. {
  348. // m_log.DebugFormat("ESCAPING PATH {0}", path);
  349. StringBuilder sb = new StringBuilder();
  350. bool singleEscapeChar = false;
  351. for (int i = 0; i < path.Length; i++)
  352. {
  353. if (path[i] == ESCAPE_CHARACTER && !singleEscapeChar)
  354. singleEscapeChar = true;
  355. else
  356. singleEscapeChar = false;
  357. if (singleEscapeChar)
  358. {
  359. if (PATH_DELIMITER == path[i])
  360. sb.Append(PATH_DELIMITER);
  361. }
  362. else
  363. {
  364. sb.Append(path[i]);
  365. }
  366. }
  367. // m_log.DebugFormat("ESCAPED PATH TO {0}", sb);
  368. return sb.ToString();
  369. }
  370. /// <summary>
  371. /// Escape an archive path.
  372. /// </summary>
  373. /// This has to be done differently from human paths because we can't leave in any "/" characters (due to
  374. /// problems if the archive is built from or extracted to a filesystem
  375. /// <param name="path"></param>
  376. /// <returns></returns>
  377. public static string EscapeArchivePath(string path)
  378. {
  379. // Only encode ampersands (for escaping anything) and / (since this is used as general dir separator).
  380. return path.Replace("&", "&amp;").Replace("/", "&#47;");
  381. }
  382. /// <summary>
  383. /// Unescape an archive path.
  384. /// </summary>
  385. /// <param name="path"></param>
  386. /// <returns></returns>
  387. public static string UnescapeArchivePath(string path)
  388. {
  389. return path.Replace("&#47;", "/").Replace("&amp;", "&");
  390. }
  391. }
  392. }