FetchInvDescHandler.cs 18 KB

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