FetchInvDescHandler.cs 21 KB

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