FetchInvDescHandler.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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;
  29. using System.Collections.Generic;
  30. using System.Linq;
  31. using System.Reflection;
  32. using System.Text;
  33. using log4net;
  34. using Nini.Config;
  35. using OpenMetaverse;
  36. using OpenMetaverse.StructuredData;
  37. using OpenSim.Framework;
  38. using OpenSim.Framework.Capabilities;
  39. using OpenSim.Region.Framework.Interfaces;
  40. using OpenSim.Framework.Servers.HttpServer;
  41. using OpenSim.Services.Interfaces;
  42. using Caps = OpenSim.Framework.Capabilities.Caps;
  43. namespace OpenSim.Capabilities.Handlers
  44. {
  45. public class FetchInvDescHandler
  46. {
  47. private static readonly ILog m_log =
  48. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  49. private IInventoryService m_InventoryService;
  50. private ILibraryService m_LibraryService;
  51. private IScene m_Scene;
  52. // private object m_fetchLock = new Object();
  53. public FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s)
  54. {
  55. m_InventoryService = invService;
  56. m_LibraryService = libService;
  57. m_Scene = s;
  58. }
  59. public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
  60. {
  61. //m_log.DebugFormat("[XXX]: FetchInventoryDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, request);
  62. ArrayList foldersrequested = null;
  63. try
  64. {
  65. Hashtable hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
  66. foldersrequested = (ArrayList)hash["folders"];
  67. hash = null;
  68. }
  69. catch (Exception e)
  70. {
  71. m_log.ErrorFormat("[FETCH INV DESC]: fail parsing request: '{0}'; path: '{1}'; exception: '{2}'", request, path, e.Message);
  72. foldersrequested = null;
  73. }
  74. if(foldersrequested == null || foldersrequested.Count == 0)
  75. return "<llsd><map><key>folders</key><array /></map></llsd>";
  76. List<LLSDFetchInventoryDescendents> folders = new List<LLSDFetchInventoryDescendents>();
  77. for (int i = 0; i < foldersrequested.Count; i++)
  78. {
  79. Hashtable inventoryhash = (Hashtable)foldersrequested[i];
  80. LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
  81. try
  82. {
  83. LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
  84. }
  85. catch (Exception e)
  86. {
  87. m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e.Message);
  88. continue;
  89. }
  90. folders.Add(llsdRequest);
  91. }
  92. foldersrequested.Clear();
  93. if(folders.Count == 0)
  94. return "<llsd><map><key>folders</key><array /></map></llsd>";
  95. List<UUID> bad_folders = new List<UUID>();
  96. int total_folders = 0;
  97. int total_items = 0;
  98. List<InventoryCollection> invcollSet = Fetch(folders, bad_folders, ref total_folders, ref total_items);
  99. //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count);
  100. int invcollSetCount = 0;
  101. if (invcollSet != null)
  102. invcollSetCount = invcollSet.Count;
  103. int mem = 8192 + ((256 * invcollSetCount +
  104. 384 * total_folders +
  105. 1024 * total_items +
  106. 128 * bad_folders.Count) & 0x7ffff000);
  107. StringBuilder lastresponse = new StringBuilder(mem);
  108. lastresponse.Append("<llsd>");
  109. if(invcollSetCount > 0)
  110. {
  111. lastresponse.Append("<map><key>folders</key><array>");
  112. int i = 0;
  113. InventoryCollection thiscoll;
  114. for(i = 0; i < invcollSetCount; i++)
  115. {
  116. thiscoll = invcollSet[i];
  117. invcollSet[i] = null;
  118. LLSDxmlEncode.AddMap(lastresponse);
  119. LLSDxmlEncode.AddElem("agent_id", thiscoll.OwnerID, lastresponse);
  120. LLSDxmlEncode.AddElem("descendents", thiscoll.Descendents, lastresponse);
  121. LLSDxmlEncode.AddElem("folder_id", thiscoll.FolderID, lastresponse);
  122. if(thiscoll.Folders == null || thiscoll.Folders.Count == 0)
  123. LLSDxmlEncode.AddEmptyArray("categories", lastresponse);
  124. else
  125. {
  126. LLSDxmlEncode.AddArray("categories", lastresponse);
  127. foreach (InventoryFolderBase invFolder in thiscoll.Folders)
  128. {
  129. LLSDxmlEncode.AddMap(lastresponse);
  130. LLSDxmlEncode.AddElem("folder_id", invFolder.ID, lastresponse);
  131. LLSDxmlEncode.AddElem("parent_id", invFolder.ParentID, lastresponse);
  132. LLSDxmlEncode.AddElem("name", invFolder.Name, lastresponse);
  133. LLSDxmlEncode.AddElem("type", invFolder.Type, lastresponse);
  134. LLSDxmlEncode.AddElem("preferred_type", (int)-1, lastresponse);
  135. LLSDxmlEncode.AddElem("version", invFolder.Version, lastresponse);
  136. LLSDxmlEncode.AddEndMap(lastresponse);
  137. }
  138. LLSDxmlEncode.AddEndArray(lastresponse);
  139. }
  140. if(thiscoll.Items == null || thiscoll.Items.Count == 0)
  141. LLSDxmlEncode.AddEmptyArray("items", lastresponse);
  142. else
  143. {
  144. LLSDxmlEncode.AddArray("items", lastresponse);
  145. foreach (InventoryItemBase invItem in thiscoll.Items)
  146. {
  147. invItem.ToLLSDxml(lastresponse);
  148. }
  149. LLSDxmlEncode.AddEndArray(lastresponse);
  150. }
  151. LLSDxmlEncode.AddElem("owner_id", thiscoll.OwnerID, lastresponse);
  152. LLSDxmlEncode.AddElem("version", thiscoll.Version, lastresponse);
  153. LLSDxmlEncode.AddEndMap(lastresponse);
  154. invcollSet[i] = null;
  155. }
  156. lastresponse.Append("</array></map>");
  157. thiscoll = null;
  158. }
  159. else
  160. {
  161. lastresponse.Append("<map><key>folders</key><array /></map>");
  162. }
  163. //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders));
  164. if(bad_folders.Count > 0)
  165. {
  166. lastresponse.Append("<map><key>bad_folders</key><array>");
  167. foreach (UUID bad in bad_folders)
  168. {
  169. lastresponse.Append("<map><key>folder_id</key><uuid>");
  170. lastresponse.Append(bad.ToString());
  171. lastresponse.Append("</uuid><key>error</key><string>Unknown</string></map>");
  172. }
  173. lastresponse.Append("</array></map>");
  174. }
  175. lastresponse.Append("</llsd>");
  176. return lastresponse.ToString();
  177. }
  178. private void AddLibraryFolders(List<LLSDFetchInventoryDescendents> libFolders, List<InventoryCollection> result, ref int total_folders, ref int total_items)
  179. {
  180. InventoryFolderImpl fold;
  181. if (m_LibraryService == null || m_LibraryService.LibraryRootFolder == null)
  182. return;
  183. foreach (LLSDFetchInventoryDescendents f in libFolders)
  184. {
  185. if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null)
  186. {
  187. InventoryCollection Collection = new InventoryCollection();
  188. // ret.Collection.Folders = new List<InventoryFolderBase>();
  189. Collection.Folders = fold.RequestListOfFolders();
  190. Collection.Items = fold.RequestListOfItems();
  191. Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner;
  192. Collection.FolderID = f.folder_id;
  193. Collection.Version = fold.Version;
  194. Collection.Descendents = Collection.Items.Count + Collection.Folders.Count;
  195. total_folders += Collection.Folders.Count;
  196. total_items += Collection.Items.Count;
  197. result.Add(Collection);
  198. //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID);
  199. }
  200. }
  201. }
  202. private List<InventoryCollection> Fetch(List<LLSDFetchInventoryDescendents> fetchFolders, List<UUID> bad_folders, ref int total_folders, ref int total_items)
  203. {
  204. //m_log.DebugFormat(
  205. // "[WEB FETCH INV DESC HANDLER]: Fetching {0} folders for owner {1}", fetchFolders.Count, fetchFolders[0].owner_id);
  206. // FIXME MAYBE: We're not handling sortOrder!
  207. List<InventoryCollection> result = new List<InventoryCollection>(32);
  208. List<LLSDFetchInventoryDescendents> libFolders = new List<LLSDFetchInventoryDescendents>(32);
  209. List<LLSDFetchInventoryDescendents> otherFolders = new List<LLSDFetchInventoryDescendents>(32);
  210. HashSet<UUID> libIDs = new HashSet<UUID>();
  211. HashSet<UUID> otherIDs = new HashSet<UUID>();
  212. bool dolib = (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null);
  213. UUID libOwner = UUID.Zero;
  214. if(dolib)
  215. libOwner = m_LibraryService.LibraryRootFolder.Owner;
  216. // Filter folder Zero right here. Some viewers (Firestorm) send request for folder Zero, which doesn't make sense
  217. // and can kill the sim (all root folders have parent_id Zero)
  218. // send something.
  219. bool doneZeroID = false;
  220. foreach(LLSDFetchInventoryDescendents f in fetchFolders)
  221. {
  222. if (f.folder_id == UUID.Zero)
  223. {
  224. if(doneZeroID)
  225. continue;
  226. doneZeroID = true;
  227. InventoryCollection Collection = new InventoryCollection();
  228. Collection.OwnerID = f.owner_id;
  229. Collection.Version = 0;
  230. Collection.FolderID = f.folder_id;
  231. Collection.Descendents = 0;
  232. result.Add(Collection);
  233. continue;
  234. }
  235. if(dolib && f.owner_id == libOwner)
  236. {
  237. if(libIDs.Contains(f.folder_id))
  238. continue;
  239. libIDs.Add(f.folder_id);
  240. libFolders.Add(f);
  241. continue;
  242. }
  243. if(otherIDs.Contains(f.folder_id))
  244. continue;
  245. otherIDs.Add(f.folder_id);
  246. otherFolders.Add(f);
  247. }
  248. fetchFolders.Clear();
  249. if(otherFolders.Count > 0)
  250. {
  251. int i = 0;
  252. //m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids));
  253. InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(otherFolders[0].owner_id, otherIDs.ToArray());
  254. if (fetchedContents == null)
  255. return null;
  256. if (fetchedContents.Length == 0)
  257. {
  258. foreach (LLSDFetchInventoryDescendents freq in otherFolders)
  259. BadFolder(freq, null, bad_folders);
  260. }
  261. else
  262. {
  263. i = 0;
  264. // Do some post-processing. May need to fetch more from inv server for links
  265. foreach (InventoryCollection contents in fetchedContents)
  266. {
  267. // Find the original request
  268. LLSDFetchInventoryDescendents freq = otherFolders[i];
  269. otherFolders[i]=null;
  270. i++;
  271. if (BadFolder(freq, contents, bad_folders))
  272. continue;
  273. if(!freq.fetch_folders)
  274. contents.Folders.Clear();
  275. if(!freq.fetch_items)
  276. contents.Items.Clear();
  277. contents.Descendents = contents.Items.Count + contents.Folders.Count;
  278. // Next: link management
  279. ProcessLinks(freq, contents);
  280. total_folders += contents.Folders.Count;
  281. total_items += contents.Items.Count;
  282. result.Add(contents);
  283. }
  284. }
  285. }
  286. if(dolib && libFolders.Count > 0)
  287. {
  288. AddLibraryFolders(libFolders, result, ref total_folders, ref total_items);
  289. }
  290. return result;
  291. }
  292. private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List<UUID> bad_folders)
  293. {
  294. if (contents == null)
  295. {
  296. bad_folders.Add(freq.folder_id);
  297. return true;
  298. }
  299. // The inventory server isn't sending FolderID in the collection...
  300. // Must fetch it individually
  301. if (contents.FolderID == UUID.Zero)
  302. {
  303. InventoryFolderBase containingFolder = m_InventoryService.GetFolder(freq.owner_id, freq.folder_id);
  304. if (containingFolder == null)
  305. {
  306. m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id);
  307. bad_folders.Add(freq.folder_id);
  308. return true;
  309. }
  310. contents.FolderID = containingFolder.ID;
  311. contents.OwnerID = containingFolder.Owner;
  312. contents.Version = containingFolder.Version;
  313. }
  314. return false;
  315. }
  316. private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollection contents)
  317. {
  318. if (contents.Items == null || contents.Items.Count == 0)
  319. return;
  320. // viewers are lasy and want a copy of the linked item sent before the link to it
  321. // look for item links
  322. List<UUID> itemIDs = new List<UUID>();
  323. foreach (InventoryItemBase item in contents.Items)
  324. {
  325. //m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType);
  326. if (item.AssetType == (int)AssetType.Link)
  327. itemIDs.Add(item.AssetID);
  328. }
  329. // get the linked if any
  330. if (itemIDs.Count > 0)
  331. {
  332. InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray());
  333. if (linked != null)
  334. {
  335. List<InventoryItemBase> linkedItems = new List<InventoryItemBase>();
  336. // check for broken
  337. foreach (InventoryItemBase linkedItem in linked)
  338. {
  339. // Take care of genuinely broken links where the target doesn't exist
  340. // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
  341. // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
  342. // rather than having to keep track of every folder requested in the recursion.
  343. if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
  344. {
  345. linkedItems.Add(linkedItem);
  346. //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder);
  347. }
  348. }
  349. // insert them
  350. if(linkedItems.Count > 0)
  351. contents.Items.InsertRange(0, linkedItems);
  352. }
  353. }
  354. }
  355. }
  356. }