InventoryArchiveUtils.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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. ///
  51. /// This method does not handle paths that contain multiple delimitors
  52. ///
  53. /// FIXME: We do not yet handle situations where folders have the same name. We could handle this by some
  54. /// XPath like expression
  55. ///
  56. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  57. ///
  58. /// <param name="inventoryService">
  59. /// Inventory service to query
  60. /// </param>
  61. /// <param name="userId">
  62. /// User id to search
  63. /// </param>
  64. /// <param name="path">
  65. /// The path to the required folder.
  66. /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned.
  67. /// </param>
  68. /// <returns>null if the folder is not found</returns>
  69. public static InventoryFolderBase FindFolderByPath(
  70. IInventoryService inventoryService, UUID userId, string path)
  71. {
  72. InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
  73. if (null == rootFolder)
  74. return null;
  75. return FindFolderByPath(inventoryService, rootFolder, path);
  76. }
  77. /// <summary>
  78. /// Find a folder given a PATH_DELIMITER delimited path starting from this folder
  79. /// </summary>
  80. ///
  81. /// This method does not handle paths that contain multiple delimitors
  82. ///
  83. /// FIXME: We do not yet handle situations where folders have the same name. We could handle this by some
  84. /// XPath like expression
  85. ///
  86. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  87. ///
  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>null if the folder is not found</returns>
  99. public static InventoryFolderBase FindFolderByPath(
  100. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  101. {
  102. if (path == string.Empty)
  103. return startFolder;
  104. path = path.Trim();
  105. if (path == PATH_DELIMITER.ToString())
  106. return startFolder;
  107. string[] components = SplitEscapedPath(path);
  108. components[0] = UnescapePath(components[0]);
  109. //string[] components = path.Split(new string[] { PATH_DELIMITER.ToString() }, 2, StringSplitOptions.None);
  110. InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID);
  111. foreach (InventoryFolderBase folder in contents.Folders)
  112. {
  113. if (folder.Name == components[0])
  114. {
  115. if (components.Length > 1)
  116. return FindFolderByPath(inventoryService, folder, components[1]);
  117. else
  118. return folder;
  119. }
  120. }
  121. // We didn't find a folder with the right name
  122. return null;
  123. }
  124. /// <summary>
  125. /// Find an item given a PATH_DELIMITOR delimited path starting from the user's root folder.
  126. ///
  127. /// This method does not handle paths that contain multiple delimitors
  128. ///
  129. /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some
  130. /// XPath like expression
  131. ///
  132. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  133. /// </summary>
  134. ///
  135. /// <param name="inventoryService">
  136. /// Inventory service to query
  137. /// </param>
  138. /// <param name="userId">
  139. /// The user to search
  140. /// </param>
  141. /// <param name="path">
  142. /// The path to the required item.
  143. /// </param>
  144. /// <returns>null if the item is not found</returns>
  145. public static InventoryItemBase FindItemByPath(
  146. IInventoryService inventoryService, UUID userId, string path)
  147. {
  148. InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
  149. if (null == rootFolder)
  150. return null;
  151. return FindItemByPath(inventoryService, rootFolder, path);
  152. }
  153. /// <summary>
  154. /// Find an item given a PATH_DELIMITOR delimited path starting from this folder.
  155. ///
  156. /// This method does not handle paths that contain multiple delimitors
  157. ///
  158. /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some
  159. /// XPath like expression
  160. ///
  161. /// FIXME: Delimitors which occur in names themselves are not currently escapable.
  162. /// </summary>
  163. ///
  164. /// <param name="inventoryService">
  165. /// Inventory service to query
  166. /// </param>
  167. /// <param name="startFolder">
  168. /// The folder from which the path starts
  169. /// </param>
  170. /// <param name="path">
  171. /// <param name="path">
  172. /// The path to the required item.
  173. /// </param>
  174. /// <returns>null if the item is not found</returns>
  175. public static InventoryItemBase FindItemByPath(
  176. IInventoryService inventoryService, InventoryFolderBase startFolder, string path)
  177. {
  178. string[] components = SplitEscapedPath(path);
  179. components[0] = UnescapePath(components[0]);
  180. //string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None);
  181. if (components.Length == 1)
  182. {
  183. // m_log.DebugFormat("FOUND SINGLE COMPONENT [{0}]", components[0]);
  184. List<InventoryItemBase> items = inventoryService.GetFolderItems(startFolder.Owner, startFolder.ID);
  185. foreach (InventoryItemBase item in items)
  186. {
  187. if (item.Name == components[0])
  188. return item;
  189. }
  190. }
  191. else
  192. {
  193. // m_log.DebugFormat("FOUND COMPONENTS [{0}] and [{1}]", components[0], components[1]);
  194. InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID);
  195. foreach (InventoryFolderBase folder in contents.Folders)
  196. {
  197. if (folder.Name == components[0])
  198. return FindItemByPath(inventoryService, folder, components[1]);
  199. }
  200. }
  201. // We didn't find an item or intermediate folder with the given name
  202. return null;
  203. }
  204. /// <summary>
  205. /// Split a human escaped path into two components if it contains an unescaped path delimiter, or one component
  206. /// if no delimiter is present
  207. /// </summary>
  208. /// <param name="path"></param>
  209. /// <returns>
  210. /// The split path. We leave the components in their originally unescaped state (though we remove the delimiter
  211. /// which originally split them if applicable).
  212. /// </returns>
  213. public static string[] SplitEscapedPath(string path)
  214. {
  215. // m_log.DebugFormat("SPLITTING PATH {0}", path);
  216. bool singleEscapeChar = false;
  217. for (int i = 0; i < path.Length; i++)
  218. {
  219. if (path[i] == ESCAPE_CHARACTER && !singleEscapeChar)
  220. {
  221. singleEscapeChar = true;
  222. }
  223. else
  224. {
  225. if (PATH_DELIMITER == path[i] && !singleEscapeChar)
  226. return new string[2] { path.Remove(i), path.Substring(i + 1) };
  227. else
  228. singleEscapeChar = false;
  229. }
  230. }
  231. // We didn't find a delimiter
  232. return new string[1] { path };
  233. }
  234. /// <summary>
  235. /// Unescapes a human escaped path. This means that "\\" goes to "\", and "\/" goes to "/"
  236. /// </summary>
  237. /// <param name="path"></param>
  238. /// <returns></returns>
  239. public static string UnescapePath(string path)
  240. {
  241. // m_log.DebugFormat("ESCAPING PATH {0}", path);
  242. StringBuilder sb = new StringBuilder();
  243. bool singleEscapeChar = false;
  244. for (int i = 0; i < path.Length; i++)
  245. {
  246. if (path[i] == ESCAPE_CHARACTER && !singleEscapeChar)
  247. singleEscapeChar = true;
  248. else
  249. singleEscapeChar = false;
  250. if (singleEscapeChar)
  251. {
  252. if (PATH_DELIMITER == path[i])
  253. sb.Append(PATH_DELIMITER);
  254. }
  255. else
  256. {
  257. sb.Append(path[i]);
  258. }
  259. }
  260. // m_log.DebugFormat("ESCAPED PATH TO {0}", sb);
  261. return sb.ToString();
  262. }
  263. /// <summary>
  264. /// Escape an archive path.
  265. /// </summary>
  266. /// This has to be done differently from human paths because we can't leave in any "/" characters (due to
  267. /// problems if the archive is built from or extracted to a filesystem
  268. /// <param name="path"></param>
  269. /// <returns></returns>
  270. public static string EscapeArchivePath(string path)
  271. {
  272. // Only encode ampersands (for escaping anything) and / (since this is used as general dir separator).
  273. return path.Replace("&", "&amp;").Replace("/", "&#47;");
  274. }
  275. /// <summary>
  276. /// Unescape an archive path.
  277. /// </summary>
  278. /// <param name="path"></param>
  279. /// <returns></returns>
  280. public static string UnescapeArchivePath(string path)
  281. {
  282. return path.Replace("&#47;", "/").Replace("&amp;", "&");
  283. }
  284. }
  285. }