OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
FetchInvDescHandler.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Linq;
32 using System.Reflection;
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;
43 
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 
51  private IInventoryService m_InventoryService;
52  private ILibraryService m_LibraryService;
53  private IScene m_Scene;
54 // private object m_fetchLock = new Object();
55 
56  public FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s)
57  {
58  m_InventoryService = invService;
59  m_LibraryService = libService;
60  m_Scene = s;
61  }
62 
63 
64  public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
65  {
66  //m_log.DebugFormat("[XXX]: FetchInventoryDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, request);
67 
68  // nasty temporary hack here, the linden client falsely
69  // identifies the uuid 00000000-0000-0000-0000-000000000000
70  // as a string which breaks us
71  //
72  // correctly mark it as a uuid
73  //
74  request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
75 
76  // another hack <integer>1</integer> results in a
77  // System.ArgumentException: Object type System.Int32 cannot
78  // be converted to target type: System.Boolean
79  //
80  request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
81  request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
82 
83  Hashtable hash = new Hashtable();
84  try
85  {
86  hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
87  }
88  catch (LLSD.LLSDParseException e)
89  {
90  m_log.ErrorFormat("[WEB FETCH INV DESC HANDLER]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
91  m_log.Error("Request: " + request);
92  }
93 
94  ArrayList foldersrequested = (ArrayList)hash["folders"];
95 
96  string response = "";
97  string bad_folders_response = "";
98 
99  List<LLSDFetchInventoryDescendents> folders = new List<LLSDFetchInventoryDescendents>();
100  for (int i = 0; i < foldersrequested.Count; i++)
101  {
102  Hashtable inventoryhash = (Hashtable)foldersrequested[i];
103 
105 
106  try
107  {
108  LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
109  }
110  catch (Exception e)
111  {
112  m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e);
113  continue;
114  }
115 
116  // Filter duplicate folder ids that bad viewers may send
117  if (folders.Find(f => f.folder_id == llsdRequest.folder_id) == null)
118  folders.Add(llsdRequest);
119 
120  }
121 
122  if (folders.Count > 0)
123  {
124  List<UUID> bad_folders = new List<UUID>();
125  List<InventoryCollectionWithDescendents> invcollSet = Fetch(folders, bad_folders);
126  //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count);
127 
128  if (invcollSet == null)
129  {
130  m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Multiple folder fetch failed. Trying old protocol.");
131 #pragma warning disable 0612
132  return FetchInventoryDescendentsRequest(foldersrequested, httpRequest, httpResponse);
133 #pragma warning restore 0612
134  }
135 
136  string inventoryitemstr = string.Empty;
137  foreach (InventoryCollectionWithDescendents icoll in invcollSet)
138  {
139  LLSDInventoryDescendents reply = ToLLSD(icoll.Collection, icoll.Descendents);
140 
141  inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
142  inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
143  inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
144 
145  response += inventoryitemstr;
146  }
147 
148  //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders));
149  foreach (UUID bad in bad_folders)
150  bad_folders_response += "<uuid>" + bad + "</uuid>";
151  }
152 
153  if (response.Length == 0)
154  {
155  /* Viewers expect a bad_folders array when not available */
156  if (bad_folders_response.Length != 0)
157  {
158  response = "<llsd><map><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
159  }
160  else
161  {
162  response = "<llsd><map><key>folders</key><array /></map></llsd>";
163  }
164  }
165  else
166  {
167  if (bad_folders_response.Length != 0)
168  {
169  response = "<llsd><map><key>folders</key><array>" + response + "</array><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
170  }
171  else
172  {
173  response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
174  }
175  }
176 
177  //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request for {0} folders. Item count {1}", folders.Count, item_count);
178  //m_log.Debug("[WEB FETCH INV DESC HANDLER] " + response);
179 
180  return response;
181 
182  }
183 
189  private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch)
190  {
193  contents.agent_id = invFetch.owner_id;
194  contents.owner_id = invFetch.owner_id;
195  contents.folder_id = invFetch.folder_id;
196 
197  reply.folders.Array.Add(contents);
199  inv.Folders = new List<InventoryFolderBase>();
200  inv.Items = new List<InventoryItemBase>();
201  int version = 0;
202  int descendents = 0;
203 
204 #pragma warning disable 0612
205  inv = Fetch(
206  invFetch.owner_id, invFetch.folder_id, invFetch.owner_id,
207  invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents);
208 #pragma warning restore 0612
209 
210  if (inv != null && inv.Folders != null)
211  {
212  foreach (InventoryFolderBase invFolder in inv.Folders)
213  {
214  contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
215  }
216 
217  descendents += inv.Folders.Count;
218  }
219 
220  if (inv != null && inv.Items != null)
221  {
222  foreach (InventoryItemBase invItem in inv.Items)
223  {
224  contents.items.Array.Add(ConvertInventoryItem(invItem));
225  }
226  }
227 
228  contents.descendents = descendents;
229  contents.version = version;
230 
231  //m_log.DebugFormat(
232  // "[WEB FETCH INV DESC HANDLER]: Replying to request for folder {0} (fetch items {1}, fetch folders {2}) with {3} items and {4} folders for agent {5}",
233  // invFetch.folder_id,
234  // invFetch.fetch_items,
235  // invFetch.fetch_folders,
236  // contents.items.Array.Count,
237  // contents.categories.Array.Count,
238  // invFetch.owner_id);
239 
240  return reply;
241  }
242 
243  private LLSDInventoryDescendents ToLLSD(InventoryCollection inv, int descendents)
244  {
247  contents.agent_id = inv.OwnerID;
248  contents.owner_id = inv.OwnerID;
249  contents.folder_id = inv.FolderID;
250 
251  reply.folders.Array.Add(contents);
252 
253  if (inv.Folders != null)
254  {
255  foreach (InventoryFolderBase invFolder in inv.Folders)
256  {
257  contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
258  }
259 
260  descendents += inv.Folders.Count;
261  }
262 
263  if (inv.Items != null)
264  {
265  foreach (InventoryItemBase invItem in inv.Items)
266  {
267  contents.items.Array.Add(ConvertInventoryItem(invItem));
268  }
269  }
270 
271  contents.descendents = descendents;
272  contents.version = inv.Version;
273 
274  return reply;
275  }
283  [Obsolete]
284  private string FetchInventoryDescendentsRequest(ArrayList foldersrequested, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
285  {
286  //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request for {0} folders", foldersrequested.Count);
287 
288  string response = "";
289  string bad_folders_response = "";
290 
291  for (int i = 0; i < foldersrequested.Count; i++)
292  {
293  string inventoryitemstr = "";
294  Hashtable inventoryhash = (Hashtable)foldersrequested[i];
295 
297 
298  try
299  {
300  LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
301  }
302  catch (Exception e)
303  {
304  m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e);
305  }
306 
307  LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
308 
309  if (null == reply)
310  {
311  bad_folders_response += "<uuid>" + llsdRequest.folder_id.ToString() + "</uuid>";
312  }
313  else
314  {
315  inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
316  inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
317  inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
318  }
319 
320  response += inventoryitemstr;
321  }
322 
323  if (response.Length == 0)
324  {
325  /* Viewers expect a bad_folders array when not available */
326  if (bad_folders_response.Length != 0)
327  {
328  response = "<llsd><map><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
329  }
330  else
331  {
332  response = "<llsd><map><key>folders</key><array /></map></llsd>";
333  }
334  }
335  else
336  {
337  if (bad_folders_response.Length != 0)
338  {
339  response = "<llsd><map><key>folders</key><array>" + response + "</array><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
340  }
341  else
342  {
343  response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
344  }
345  }
346 
347  // m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request");
348  //m_log.Debug("[WEB FETCH INV DESC HANDLER] "+response);
349 
350  return response;
351 
352  // }
353  }
354 
366  [Obsolete]
367  private InventoryCollection Fetch(
368  UUID agentID, UUID folderID, UUID ownerID,
369  bool fetchFolders, bool fetchItems, int sortOrder, out int version, out int descendents)
370  {
371  //m_log.DebugFormat(
372  // "[WEB FETCH INV DESC HANDLER]: Fetching folders ({0}), items ({1}) from {2} for agent {3}",
373  // fetchFolders, fetchItems, folderID, agentID);
374 
375  // FIXME MAYBE: We're not handling sortOrder!
376 
377  version = 0;
378  descendents = 0;
379 
380  InventoryFolderImpl fold;
381  if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null && agentID == m_LibraryService.LibraryRootFolder.Owner)
382  {
383  if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(folderID)) != null)
384  {
386  ret.Folders = new List<InventoryFolderBase>();
387  ret.Items = fold.RequestListOfItems();
388  descendents = ret.Folders.Count + ret.Items.Count;
389 
390  return ret;
391  }
392  }
393 
394  InventoryCollection contents = new InventoryCollection();
395 
396  if (folderID != UUID.Zero)
397  {
398  InventoryCollection fetchedContents = m_InventoryService.GetFolderContent(agentID, folderID);
399 
400  if (fetchedContents == null)
401  {
402  m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of folder {0} for user {1}", folderID, agentID);
403  return contents;
404  }
405  contents = fetchedContents;
406  InventoryFolderBase containingFolder = new InventoryFolderBase();
407  containingFolder.ID = folderID;
408  containingFolder.Owner = agentID;
409  containingFolder = m_InventoryService.GetFolder(containingFolder);
410 
411  if (containingFolder != null)
412  {
413  //m_log.DebugFormat(
414  // "[WEB FETCH INV DESC HANDLER]: Retrieved folder {0} {1} for agent id {2}",
415  // containingFolder.Name, containingFolder.ID, agentID);
416 
417  version = containingFolder.Version;
418 
419  if (fetchItems && containingFolder.Type != (short)FolderType.Trash)
420  {
421  List<InventoryItemBase> itemsToReturn = contents.Items;
422  List<InventoryItemBase> originalItems = new List<InventoryItemBase>(itemsToReturn);
423 
424  // descendents must only include the links, not the linked items we add
425  descendents = originalItems.Count;
426 
427  // Add target items for links in this folder before the links themselves.
428  foreach (InventoryItemBase item in originalItems)
429  {
430  if (item.AssetType == (int)AssetType.Link)
431  {
432  InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
433 
434  // Take care of genuinely broken links where the target doesn't exist
435  // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
436  // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
437  // rather than having to keep track of every folder requested in the recursion.
438  if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
439  itemsToReturn.Insert(0, linkedItem);
440  }
441  }
442 
443  // Now scan for folder links and insert the items they target and those links at the head of the return data
444 
445 /* dont send contents of LinkFolders.
446 from docs seems this was never a spec
447 
448  foreach (InventoryItemBase item in originalItems)
449  {
450  if (item.AssetType == (int)AssetType.LinkFolder)
451  {
452  InventoryCollection linkedFolderContents = m_InventoryService.GetFolderContent(ownerID, item.AssetID);
453  List<InventoryItemBase> links = linkedFolderContents.Items;
454 
455  itemsToReturn.InsertRange(0, links);
456 
457  foreach (InventoryItemBase link in linkedFolderContents.Items)
458  {
459  // Take care of genuinely broken links where the target doesn't exist
460  // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
461  // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
462  // rather than having to keep track of every folder requested in the recursion.
463  if (link != null)
464  {
465 // m_log.DebugFormat(
466 // "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3}",
467 // link.Name, (AssetType)link.AssetType, item.AssetID, containingFolder.Name);
468 
469  InventoryItemBase linkedItem
470  = m_InventoryService.GetItem(new InventoryItemBase(link.AssetID));
471 
472  if (linkedItem != null)
473  itemsToReturn.Insert(0, linkedItem);
474  }
475  }
476  }
477  }
478 */
479  }
480 
481 // foreach (InventoryItemBase item in contents.Items)
482 // {
483 // m_log.DebugFormat(
484 // "[WEB FETCH INV DESC HANDLER]: Returning item {0}, type {1}, parent {2} in {3} {4}",
485 // item.Name, (AssetType)item.AssetType, item.Folder, containingFolder.Name, containingFolder.ID);
486 // }
487 
488  // =====
489 
490 //
491 // foreach (InventoryItemBase linkedItem in linkedItemsToAdd)
492 // {
493 // m_log.DebugFormat(
494 // "[WEB FETCH INV DESC HANDLER]: Inserted linked item {0} for link in folder {1} for agent {2}",
495 // linkedItem.Name, folderID, agentID);
496 //
497 // contents.Items.Add(linkedItem);
498 // }
499 //
500 // // If the folder requested contains links, then we need to send those folders first, otherwise the links
501 // // will be broken in the viewer.
502 // HashSet<UUID> linkedItemFolderIdsToSend = new HashSet<UUID>();
503 // foreach (InventoryItemBase item in contents.Items)
504 // {
505 // if (item.AssetType == (int)AssetType.Link)
506 // {
507 // InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
508 //
509 // // Take care of genuinely broken links where the target doesn't exist
510 // // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
511 // // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
512 // // rather than having to keep track of every folder requested in the recursion.
513 // if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
514 // {
515 // // We don't need to send the folder if source and destination of the link are in the same
516 // // folder.
517 // if (linkedItem.Folder != containingFolder.ID)
518 // linkedItemFolderIdsToSend.Add(linkedItem.Folder);
519 // }
520 // }
521 // }
522 //
523 // foreach (UUID linkedItemFolderId in linkedItemFolderIdsToSend)
524 // {
525 // m_log.DebugFormat(
526 // "[WEB FETCH INV DESC HANDLER]: Recursively fetching folder {0} linked by item in folder {1} for agent {2}",
527 // linkedItemFolderId, folderID, agentID);
528 //
529 // int dummyVersion;
530 // InventoryCollection linkedCollection
531 // = Fetch(
532 // agentID, linkedItemFolderId, ownerID, fetchFolders, fetchItems, sortOrder, out dummyVersion);
533 //
534 // InventoryFolderBase linkedFolder = new InventoryFolderBase(linkedItemFolderId);
535 // linkedFolder.Owner = agentID;
536 // linkedFolder = m_InventoryService.GetFolder(linkedFolder);
537 //
539 //
540 // contents.Folders.Add(linkedFolder);
541 // contents.Items.AddRange(linkedCollection.Items);
542 // }
543 // }
544  }
545  }
546  else
547  {
548  // Lost items don't really need a version
549  version = 1;
550  }
551 
552  return contents;
553 
554  }
555 
556  private void AddLibraryFolders(List<LLSDFetchInventoryDescendents> fetchFolders, List<InventoryCollectionWithDescendents> result)
557  {
558  InventoryFolderImpl fold;
559  if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null)
560  {
561  List<LLSDFetchInventoryDescendents> libfolders = fetchFolders.FindAll(f => f.owner_id == m_LibraryService.LibraryRootFolder.Owner);
562  fetchFolders.RemoveAll(f => libfolders.Contains(f));
563 
564  //m_log.DebugFormat("[XXX]: Found {0} library folders in request", libfolders.Count);
565 
566  foreach (LLSDFetchInventoryDescendents f in libfolders)
567  {
568  if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null)
569  {
570  InventoryCollectionWithDescendents ret = new InventoryCollectionWithDescendents();
571  ret.Collection = new InventoryCollection();
572  ret.Collection.Folders = new List<InventoryFolderBase>();
573  ret.Collection.Items = fold.RequestListOfItems();
574  ret.Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner;
575  ret.Collection.FolderID = f.folder_id;
576  ret.Collection.Version = fold.Version;
577 
578  ret.Descendents = ret.Collection.Items.Count;
579  result.Add(ret);
580 
581  //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID);
582  }
583  }
584  }
585  }
586 
587  private List<InventoryCollectionWithDescendents> Fetch(List<LLSDFetchInventoryDescendents> fetchFolders, List<UUID> bad_folders)
588  {
589  //m_log.DebugFormat(
590  // "[WEB FETCH INV DESC HANDLER]: Fetching {0} folders for owner {1}", fetchFolders.Count, fetchFolders[0].owner_id);
591 
592  // FIXME MAYBE: We're not handling sortOrder!
593 
594  List<InventoryCollectionWithDescendents> result = new List<InventoryCollectionWithDescendents>();
595 
596  AddLibraryFolders(fetchFolders, result);
597 
598  // Filter folder Zero right here. Some viewers (Firestorm) send request for folder Zero, which doesn't make sense
599  // and can kill the sim (all root folders have parent_id Zero)
600  LLSDFetchInventoryDescendents zero = fetchFolders.Find(f => f.folder_id == UUID.Zero);
601  if (zero != null)
602  {
603  fetchFolders.Remove(zero);
604  BadFolder(zero, null, bad_folders);
605  }
606 
607  if (fetchFolders.Count > 0)
608  {
609  UUID[] fids = new UUID[fetchFolders.Count];
610  int i = 0;
611  foreach (LLSDFetchInventoryDescendents f in fetchFolders)
612  fids[i++] = f.folder_id;
613 
614  //m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids));
615 
616  InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(fetchFolders[0].owner_id, fids);
617 
618  if (fetchedContents == null || (fetchedContents != null && fetchedContents.Length == 0))
619  {
620  m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of multiple folders for user {0}", fetchFolders[0].owner_id);
621  foreach (LLSDFetchInventoryDescendents freq in fetchFolders)
622  BadFolder(freq, null, bad_folders);
623  return null;
624  }
625 
626  i = 0;
627  // Do some post-processing. May need to fetch more from inv server for links
628  foreach (InventoryCollection contents in fetchedContents)
629  {
630  // Find the original request
631  LLSDFetchInventoryDescendents freq = fetchFolders[i++];
632 
633  InventoryCollectionWithDescendents coll = new InventoryCollectionWithDescendents();
634  coll.Collection = contents;
635 
636  if (BadFolder(freq, contents, bad_folders))
637  continue;
638 
639  // Next: link management
640  ProcessLinks(freq, coll);
641 
642  result.Add(coll);
643  }
644  }
645 
646  return result;
647  }
648 
649  private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List<UUID> bad_folders)
650  {
651  bool bad = false;
652  if (contents == null)
653  {
654  bad_folders.Add(freq.folder_id);
655  bad = true;
656  }
657 
658  // The inventory server isn't sending FolderID in the collection...
659  // Must fetch it individually
660  else if (contents.FolderID == UUID.Zero)
661  {
662  InventoryFolderBase containingFolder = new InventoryFolderBase();
663  containingFolder.ID = freq.folder_id;
664  containingFolder.Owner = freq.owner_id;
665  containingFolder = m_InventoryService.GetFolder(containingFolder);
666 
667  if (containingFolder != null)
668  {
669  contents.FolderID = containingFolder.ID;
670  contents.OwnerID = containingFolder.Owner;
671  contents.Version = containingFolder.Version;
672  }
673  else
674  {
675  // Was it really a request for folder Zero?
676  // This is an overkill, but Firestorm really asks for folder Zero.
677  // I'm leaving the code here for the time being, but commented.
678  if (freq.folder_id == UUID.Zero)
679  {
680  //coll.Collection.OwnerID = freq.owner_id;
681  //coll.Collection.FolderID = contents.FolderID;
682  //containingFolder = m_InventoryService.GetRootFolder(freq.owner_id);
683  //if (containingFolder != null)
684  //{
685  // m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Request for parent of folder {0}", containingFolder.ID);
686  // coll.Collection.Folders.Clear();
687  // coll.Collection.Folders.Add(containingFolder);
688  // if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null)
689  // {
690  // InventoryFolderBase lib = new InventoryFolderBase(m_LibraryService.LibraryRootFolder.ID, m_LibraryService.LibraryRootFolder.Owner);
691  // lib.Name = m_LibraryService.LibraryRootFolder.Name;
692  // lib.Type = m_LibraryService.LibraryRootFolder.Type;
693  // lib.Version = m_LibraryService.LibraryRootFolder.Version;
694  // coll.Collection.Folders.Add(lib);
695  // }
696  // coll.Collection.Items.Clear();
697  //}
698  }
699  else
700  {
701  m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id);
702  bad_folders.Add(freq.folder_id);
703  }
704  bad = true;
705  }
706  }
707 
708  return bad;
709  }
710 
711  private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollectionWithDescendents coll)
712  {
713  InventoryCollection contents = coll.Collection;
714 
715  if (freq.fetch_items && contents.Items != null)
716  {
717  List<InventoryItemBase> itemsToReturn = contents.Items;
718 
719  // descendents must only include the links, not the linked items we add
720  coll.Descendents = itemsToReturn.Count;
721 
722  // Add target items for links in this folder before the links themselves.
723  List<UUID> itemIDs = new List<UUID>();
724  List<UUID> folderIDs = new List<UUID>();
725  foreach (InventoryItemBase item in itemsToReturn)
726  {
727  //m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType);
728  if (item.AssetType == (int)AssetType.Link)
729  itemIDs.Add(item.AssetID);
730 
731 // else if (item.AssetType == (int)AssetType.LinkFolder)
732 // folderIDs.Add(item.AssetID);
733  }
734 
735  //m_log.DebugFormat("[XXX]: folder {0} has {1} links and {2} linkfolders", contents.FolderID, itemIDs.Count, folderIDs.Count);
736 
737  // Scan for folder links and insert the items they target and those links at the head of the return data
738  if (folderIDs.Count > 0)
739  {
740  InventoryCollection[] linkedFolders = m_InventoryService.GetMultipleFoldersContent(coll.Collection.OwnerID, folderIDs.ToArray());
741  foreach (InventoryCollection linkedFolderContents in linkedFolders)
742  {
743  if (linkedFolderContents == null)
744  continue;
745 
746  List<InventoryItemBase> links = linkedFolderContents.Items;
747 
748  itemsToReturn.InsertRange(0, links);
749 
750  }
751  }
752 
753  if (itemIDs.Count > 0)
754  {
755  InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray());
756  if (linked == null)
757  {
758  // OMG!!! One by one!!! This is fallback code, in case the backend isn't updated
759  m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: GetMultipleItems failed. Falling back to fetching inventory items one by one.");
760  linked = new InventoryItemBase[itemIDs.Count];
761  int i = 0;
763  item.Owner = freq.owner_id;
764  foreach (UUID id in itemIDs)
765  {
766  item.ID = id;
767  linked[i++] = m_InventoryService.GetItem(item);
768  }
769  }
770 
771  //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Processing folder {0}. Existing items:", freq.folder_id);
772  //foreach (InventoryItemBase item in itemsToReturn)
773  // m_log.DebugFormat("[XXX]: {0} {1} {2}", item.Name, item.AssetType, item.Folder);
774 
775  if (linked != null)
776  {
777  foreach (InventoryItemBase linkedItem in linked)
778  {
779  // Take care of genuinely broken links where the target doesn't exist
780  // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
781  // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
782  // rather than having to keep track of every folder requested in the recursion.
783  if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
784  {
785  itemsToReturn.Insert(0, linkedItem);
786  //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder);
787  }
788  }
789  }
790  }
791  }
792 
793  }
794 
800  private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder)
801  {
802  LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder();
803  llsdFolder.folder_id = invFolder.ID;
804  llsdFolder.parent_id = invFolder.ParentID;
805  llsdFolder.name = invFolder.Name;
806  llsdFolder.type = invFolder.Type;
807  llsdFolder.preferred_type = -1;
808 
809  return llsdFolder;
810  }
811 
817  private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem)
818  {
819  LLSDInventoryItem llsdItem = new LLSDInventoryItem();
820  llsdItem.asset_id = invItem.AssetID;
821  llsdItem.created_at = invItem.CreationDate;
822  llsdItem.desc = invItem.Description;
823  llsdItem.flags = (int)invItem.Flags;
824  llsdItem.item_id = invItem.ID;
825  llsdItem.name = invItem.Name;
826  llsdItem.parent_id = invItem.Folder;
827  llsdItem.type = invItem.AssetType;
828  llsdItem.inv_type = invItem.InvType;
829 
830  llsdItem.permissions = new LLSDPermissions();
831  llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid;
832  llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions;
833  llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions;
834  llsdItem.permissions.group_id = invItem.GroupID;
835  llsdItem.permissions.group_mask = (int)invItem.GroupPermissions;
836  llsdItem.permissions.is_owner_group = invItem.GroupOwned;
837  llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions;
838  llsdItem.permissions.owner_id = invItem.Owner;
839  llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
840  llsdItem.sale_info = new LLSDSaleInfo();
841  llsdItem.sale_info.sale_price = invItem.SalePrice;
842  llsdItem.sale_info.sale_type = invItem.SaleType;
843 
844  return llsdItem;
845  }
846  }
847 
849  {
851  public int Descendents;
852  }
853 }
OpenSim.Server.Handlers.Simulation.Utils Utils
InventoryFolderImpl FindFolder(UUID folderID)
Returns the folder requested if it is this folder or is a descendent of this folder. The search is depth first.
FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s)
OpenSim.Framework.Capabilities.Caps Caps
Inventory Item - contains all the properties associated with an individual inventory piece...
UUID ID
A UUID containing the ID for the inventory node itself
virtual string Name
The name of the node (64 characters or less)
List< InventoryFolderBase > Folders
string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
Used to serialize a whole inventory for transfer over the network.