/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using log4net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Capabilities; using OpenSim.Region.Framework.Interfaces; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using Caps = OpenSim.Framework.Capabilities.Caps; namespace OpenSim.Capabilities.Handlers { public class FetchInvDescHandler { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IInventoryService m_InventoryService; private ILibraryService m_LibraryService; private IScene m_Scene; // private object m_fetchLock = new Object(); public FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s) { m_InventoryService = invService; m_LibraryService = libService; m_Scene = s; } public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { //m_log.DebugFormat("[XXX]: FetchInventoryDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, request); // nasty temporary hack here, the linden client falsely // identifies the uuid 00000000-0000-0000-0000-000000000000 // as a string which breaks us // // correctly mark it as a uuid // request = request.Replace("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); // another hack 1 results in a // System.ArgumentException: Object type System.Int32 cannot // be converted to target type: System.Boolean // request = request.Replace("fetch_folders0", "fetch_folders0"); request = request.Replace("fetch_folders1", "fetch_folders1"); Hashtable hash = new Hashtable(); try { hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); } catch (LLSD.LLSDParseException e) { m_log.ErrorFormat("[WEB FETCH INV DESC HANDLER]: Fetch error: {0}{1}" + e.Message, e.StackTrace); m_log.Error("Request: " + request); } ArrayList foldersrequested = (ArrayList)hash["folders"]; string response = ""; string bad_folders_response = ""; List folders = new List(); for (int i = 0; i < foldersrequested.Count; i++) { Hashtable inventoryhash = (Hashtable)foldersrequested[i]; LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); try { LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); } catch (Exception e) { m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e); continue; } // Filter duplicate folder ids that bad viewers may send if (folders.Find(f => f.folder_id == llsdRequest.folder_id) == null) folders.Add(llsdRequest); } if (folders.Count > 0) { List bad_folders = new List(); List invcollSet = Fetch(folders, bad_folders); //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count); if (invcollSet == null) { m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Multiple folder fetch failed. Trying old protocol."); #pragma warning disable 0612 return FetchInventoryDescendentsRequest(foldersrequested, httpRequest, httpResponse); #pragma warning restore 0612 } string inventoryitemstr = string.Empty; foreach (InventoryCollectionWithDescendents icoll in invcollSet) { LLSDInventoryDescendents reply = ToLLSD(icoll.Collection, icoll.Descendents); inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); inventoryitemstr = inventoryitemstr.Replace("folders", ""); inventoryitemstr = inventoryitemstr.Replace("", ""); response += inventoryitemstr; } //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders)); foreach (UUID bad in bad_folders) bad_folders_response += "" + bad + ""; } if (response.Length == 0) { /* Viewers expect a bad_folders array when not available */ if (bad_folders_response.Length != 0) { response = "bad_folders" + bad_folders_response + ""; } else { response = "folders"; } } else { if (bad_folders_response.Length != 0) { response = "folders" + response + "bad_folders" + bad_folders_response + ""; } else { response = "folders" + response + ""; } } //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request for {0} folders. Item count {1}", folders.Count, item_count); //m_log.Debug("[WEB FETCH INV DESC HANDLER] " + response); return response; } /// /// Construct an LLSD reply packet to a CAPS inventory request /// /// /// private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch) { LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); contents.agent_id = invFetch.owner_id; contents.owner_id = invFetch.owner_id; contents.folder_id = invFetch.folder_id; reply.folders.Array.Add(contents); InventoryCollection inv = new InventoryCollection(); inv.Folders = new List(); inv.Items = new List(); int version = 0; int descendents = 0; #pragma warning disable 0612 inv = Fetch( invFetch.owner_id, invFetch.folder_id, invFetch.owner_id, invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents); #pragma warning restore 0612 if (inv != null && inv.Folders != null) { foreach (InventoryFolderBase invFolder in inv.Folders) { contents.categories.Array.Add(ConvertInventoryFolder(invFolder)); } descendents += inv.Folders.Count; } if (inv != null && inv.Items != null) { foreach (InventoryItemBase invItem in inv.Items) { contents.items.Array.Add(ConvertInventoryItem(invItem)); } } contents.descendents = descendents; contents.version = version; //m_log.DebugFormat( // "[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}", // invFetch.folder_id, // invFetch.fetch_items, // invFetch.fetch_folders, // contents.items.Array.Count, // contents.categories.Array.Count, // invFetch.owner_id); return reply; } private LLSDInventoryDescendents ToLLSD(InventoryCollection inv, int descendents) { LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); contents.agent_id = inv.OwnerID; contents.owner_id = inv.OwnerID; contents.folder_id = inv.FolderID; reply.folders.Array.Add(contents); if (inv.Folders != null) { foreach (InventoryFolderBase invFolder in inv.Folders) { contents.categories.Array.Add(ConvertInventoryFolder(invFolder)); } descendents += inv.Folders.Count; } if (inv.Items != null) { foreach (InventoryItemBase invItem in inv.Items) { contents.items.Array.Add(ConvertInventoryItem(invItem)); } } contents.descendents = descendents; contents.version = inv.Version; return reply; } /// /// Old style. Soon to be deprecated. /// /// /// /// /// [Obsolete] private string FetchInventoryDescendentsRequest(ArrayList foldersrequested, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request for {0} folders", foldersrequested.Count); string response = ""; string bad_folders_response = ""; for (int i = 0; i < foldersrequested.Count; i++) { string inventoryitemstr = ""; Hashtable inventoryhash = (Hashtable)foldersrequested[i]; LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); try { LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); } catch (Exception e) { m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e); } LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); if (null == reply) { bad_folders_response += "" + llsdRequest.folder_id.ToString() + ""; } else { inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); inventoryitemstr = inventoryitemstr.Replace("folders", ""); inventoryitemstr = inventoryitemstr.Replace("", ""); } response += inventoryitemstr; } if (response.Length == 0) { /* Viewers expect a bad_folders array when not available */ if (bad_folders_response.Length != 0) { response = "bad_folders" + bad_folders_response + ""; } else { response = "folders"; } } else { if (bad_folders_response.Length != 0) { response = "folders" + response + "bad_folders" + bad_folders_response + ""; } else { response = "folders" + response + ""; } } // m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request"); //m_log.Debug("[WEB FETCH INV DESC HANDLER] "+response); return response; // } } /// /// Handle the caps inventory descendents fetch. /// /// /// /// /// /// /// /// /// An empty InventoryCollection if the inventory look up failed [Obsolete] private InventoryCollection Fetch( UUID agentID, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder, out int version, out int descendents) { //m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Fetching folders ({0}), items ({1}) from {2} for agent {3}", // fetchFolders, fetchItems, folderID, agentID); // FIXME MAYBE: We're not handling sortOrder! version = 0; descendents = 0; InventoryFolderImpl fold; if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null && agentID == m_LibraryService.LibraryRootFolder.Owner) { if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(folderID)) != null) { InventoryCollection ret = new InventoryCollection(); ret.Folders = new List(); ret.Items = fold.RequestListOfItems(); descendents = ret.Folders.Count + ret.Items.Count; return ret; } } InventoryCollection contents = new InventoryCollection(); if (folderID != UUID.Zero) { InventoryCollection fetchedContents = m_InventoryService.GetFolderContent(agentID, folderID); if (fetchedContents == null) { m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of folder {0} for user {1}", folderID, agentID); return contents; } contents = fetchedContents; InventoryFolderBase containingFolder = m_InventoryService.GetFolder(agentID, folderID); if (containingFolder != null) { //m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Retrieved folder {0} {1} for agent id {2}", // containingFolder.Name, containingFolder.ID, agentID); version = containingFolder.Version; if (fetchItems && containingFolder.Type != (short)FolderType.Trash) { List itemsToReturn = contents.Items; List originalItems = new List(itemsToReturn); // descendents must only include the links, not the linked items we add descendents = originalItems.Count; // Add target items for links in this folder before the links themselves. foreach (InventoryItemBase item in originalItems) { if (item.AssetType == (int)AssetType.Link) { InventoryItemBase linkedItem = m_InventoryService.GetItem(agentID, item.AssetID); // Take care of genuinely broken links where the target doesn't exist // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles // rather than having to keep track of every folder requested in the recursion. if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link) itemsToReturn.Insert(0, linkedItem); } } // Now scan for folder links and insert the items they target and those links at the head of the return data /* dont send contents of LinkFolders. from docs seems this was never a spec foreach (InventoryItemBase item in originalItems) { if (item.AssetType == (int)AssetType.LinkFolder) { InventoryCollection linkedFolderContents = m_InventoryService.GetFolderContent(ownerID, item.AssetID); List links = linkedFolderContents.Items; itemsToReturn.InsertRange(0, links); foreach (InventoryItemBase link in linkedFolderContents.Items) { // Take care of genuinely broken links where the target doesn't exist // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles // rather than having to keep track of every folder requested in the recursion. if (link != null) { // m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3}", // link.Name, (AssetType)link.AssetType, item.AssetID, containingFolder.Name); InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(link.AssetID)); if (linkedItem != null) itemsToReturn.Insert(0, linkedItem); } } } } */ } // foreach (InventoryItemBase item in contents.Items) // { // m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Returning item {0}, type {1}, parent {2} in {3} {4}", // item.Name, (AssetType)item.AssetType, item.Folder, containingFolder.Name, containingFolder.ID); // } // ===== // // foreach (InventoryItemBase linkedItem in linkedItemsToAdd) // { // m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Inserted linked item {0} for link in folder {1} for agent {2}", // linkedItem.Name, folderID, agentID); // // contents.Items.Add(linkedItem); // } // // // If the folder requested contains links, then we need to send those folders first, otherwise the links // // will be broken in the viewer. // HashSet linkedItemFolderIdsToSend = new HashSet(); // foreach (InventoryItemBase item in contents.Items) // { // if (item.AssetType == (int)AssetType.Link) // { // InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID)); // // // Take care of genuinely broken links where the target doesn't exist // // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, // // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles // // rather than having to keep track of every folder requested in the recursion. // if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link) // { // // We don't need to send the folder if source and destination of the link are in the same // // folder. // if (linkedItem.Folder != containingFolder.ID) // linkedItemFolderIdsToSend.Add(linkedItem.Folder); // } // } // } // // foreach (UUID linkedItemFolderId in linkedItemFolderIdsToSend) // { // m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Recursively fetching folder {0} linked by item in folder {1} for agent {2}", // linkedItemFolderId, folderID, agentID); // // int dummyVersion; // InventoryCollection linkedCollection // = Fetch( // agentID, linkedItemFolderId, ownerID, fetchFolders, fetchItems, sortOrder, out dummyVersion); // // InventoryFolderBase linkedFolder = new InventoryFolderBase(linkedItemFolderId); // linkedFolder.Owner = agentID; // linkedFolder = m_InventoryService.GetFolder(linkedFolder); // //// contents.Folders.AddRange(linkedCollection.Folders); // // contents.Folders.Add(linkedFolder); // contents.Items.AddRange(linkedCollection.Items); // } // } } } else { // Lost items don't really need a version version = 1; } return contents; } private void AddLibraryFolders(List fetchFolders, List result) { InventoryFolderImpl fold; if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null) { List libfolders = fetchFolders.FindAll(f => f.owner_id == m_LibraryService.LibraryRootFolder.Owner); fetchFolders.RemoveAll(f => libfolders.Contains(f)); //m_log.DebugFormat("[XXX]: Found {0} library folders in request", libfolders.Count); foreach (LLSDFetchInventoryDescendents f in libfolders) { if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null) { InventoryCollectionWithDescendents ret = new InventoryCollectionWithDescendents(); ret.Collection = new InventoryCollection(); ret.Collection.Folders = new List(); ret.Collection.Items = fold.RequestListOfItems(); ret.Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner; ret.Collection.FolderID = f.folder_id; ret.Collection.Version = fold.Version; ret.Descendents = ret.Collection.Items.Count; result.Add(ret); //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID); } } } } private List Fetch(List fetchFolders, List bad_folders) { //m_log.DebugFormat( // "[WEB FETCH INV DESC HANDLER]: Fetching {0} folders for owner {1}", fetchFolders.Count, fetchFolders[0].owner_id); // FIXME MAYBE: We're not handling sortOrder! List result = new List(); AddLibraryFolders(fetchFolders, result); // Filter folder Zero right here. Some viewers (Firestorm) send request for folder Zero, which doesn't make sense // and can kill the sim (all root folders have parent_id Zero) LLSDFetchInventoryDescendents zero = fetchFolders.Find(f => f.folder_id == UUID.Zero); if (zero != null) { fetchFolders.Remove(zero); BadFolder(zero, null, bad_folders); } if (fetchFolders.Count > 0) { UUID[] fids = new UUID[fetchFolders.Count]; int i = 0; foreach (LLSDFetchInventoryDescendents f in fetchFolders) fids[i++] = f.folder_id; //m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids)); InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(fetchFolders[0].owner_id, fids); if (fetchedContents == null || (fetchedContents != null && fetchedContents.Length == 0)) { m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of multiple folders for user {0}", fetchFolders[0].owner_id); foreach (LLSDFetchInventoryDescendents freq in fetchFolders) BadFolder(freq, null, bad_folders); return null; } i = 0; // Do some post-processing. May need to fetch more from inv server for links foreach (InventoryCollection contents in fetchedContents) { // Find the original request LLSDFetchInventoryDescendents freq = fetchFolders[i++]; InventoryCollectionWithDescendents coll = new InventoryCollectionWithDescendents(); coll.Collection = contents; if (BadFolder(freq, contents, bad_folders)) continue; // Next: link management ProcessLinks(freq, coll); result.Add(coll); } } return result; } private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List bad_folders) { bool bad = false; if (contents == null) { bad_folders.Add(freq.folder_id); bad = true; } // The inventory server isn't sending FolderID in the collection... // Must fetch it individually else if (contents.FolderID == UUID.Zero) { InventoryFolderBase containingFolder = m_InventoryService.GetFolder(freq.owner_id, freq.folder_id); if (containingFolder != null) { contents.FolderID = containingFolder.ID; contents.OwnerID = containingFolder.Owner; contents.Version = containingFolder.Version; } else { // Was it really a request for folder Zero? // This is an overkill, but Firestorm really asks for folder Zero. // I'm leaving the code here for the time being, but commented. if (freq.folder_id == UUID.Zero) { //coll.Collection.OwnerID = freq.owner_id; //coll.Collection.FolderID = contents.FolderID; //containingFolder = m_InventoryService.GetRootFolder(freq.owner_id); //if (containingFolder != null) //{ // m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Request for parent of folder {0}", containingFolder.ID); // coll.Collection.Folders.Clear(); // coll.Collection.Folders.Add(containingFolder); // if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null) // { // InventoryFolderBase lib = new InventoryFolderBase(m_LibraryService.LibraryRootFolder.ID, m_LibraryService.LibraryRootFolder.Owner); // lib.Name = m_LibraryService.LibraryRootFolder.Name; // lib.Type = m_LibraryService.LibraryRootFolder.Type; // lib.Version = m_LibraryService.LibraryRootFolder.Version; // coll.Collection.Folders.Add(lib); // } // coll.Collection.Items.Clear(); //} } else { m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id); bad_folders.Add(freq.folder_id); } bad = true; } } return bad; } private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollectionWithDescendents coll) { InventoryCollection contents = coll.Collection; if (freq.fetch_items && contents.Items != null) { List itemsToReturn = contents.Items; // descendents must only include the links, not the linked items we add coll.Descendents = itemsToReturn.Count; // Add target items for links in this folder before the links themselves. List itemIDs = new List(); List folderIDs = new List(); foreach (InventoryItemBase item in itemsToReturn) { //m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType); if (item.AssetType == (int)AssetType.Link) itemIDs.Add(item.AssetID); // else if (item.AssetType == (int)AssetType.LinkFolder) // folderIDs.Add(item.AssetID); } //m_log.DebugFormat("[XXX]: folder {0} has {1} links and {2} linkfolders", contents.FolderID, itemIDs.Count, folderIDs.Count); // Scan for folder links and insert the items they target and those links at the head of the return data if (folderIDs.Count > 0) { InventoryCollection[] linkedFolders = m_InventoryService.GetMultipleFoldersContent(coll.Collection.OwnerID, folderIDs.ToArray()); foreach (InventoryCollection linkedFolderContents in linkedFolders) { if (linkedFolderContents == null) continue; List links = linkedFolderContents.Items; itemsToReturn.InsertRange(0, links); } } if (itemIDs.Count > 0) { InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray()); if (linked == null) { // OMG!!! One by one!!! This is fallback code, in case the backend isn't updated m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: GetMultipleItems failed. Falling back to fetching inventory items one by one."); linked = new InventoryItemBase[itemIDs.Count]; int i = 0; foreach (UUID id in itemIDs) { linked[i++] = m_InventoryService.GetItem(freq.owner_id, id); } } //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Processing folder {0}. Existing items:", freq.folder_id); //foreach (InventoryItemBase item in itemsToReturn) // m_log.DebugFormat("[XXX]: {0} {1} {2}", item.Name, item.AssetType, item.Folder); if (linked != null) { foreach (InventoryItemBase linkedItem in linked) { // Take care of genuinely broken links where the target doesn't exist // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles // rather than having to keep track of every folder requested in the recursion. if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link) { itemsToReturn.Insert(0, linkedItem); //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder); } } } } } } /// /// Convert an internal inventory folder object into an LLSD object. /// /// /// private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder) { LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder(); llsdFolder.folder_id = invFolder.ID; llsdFolder.parent_id = invFolder.ParentID; llsdFolder.name = invFolder.Name; llsdFolder.type = invFolder.Type; llsdFolder.preferred_type = -1; return llsdFolder; } /// /// Convert an internal inventory item object into an LLSD object. /// /// /// private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem) { LLSDInventoryItem llsdItem = new LLSDInventoryItem(); llsdItem.asset_id = invItem.AssetID; llsdItem.created_at = invItem.CreationDate; llsdItem.desc = invItem.Description; llsdItem.flags = (int)invItem.Flags; llsdItem.item_id = invItem.ID; llsdItem.name = invItem.Name; llsdItem.parent_id = invItem.Folder; llsdItem.type = invItem.AssetType; llsdItem.inv_type = invItem.InvType; llsdItem.permissions = new LLSDPermissions(); llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid; llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions; llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions; llsdItem.permissions.group_id = invItem.GroupID; llsdItem.permissions.group_mask = (int)invItem.GroupPermissions; llsdItem.permissions.is_owner_group = invItem.GroupOwned; llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions; llsdItem.permissions.owner_id = invItem.Owner; llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions; llsdItem.sale_info = new LLSDSaleInfo(); llsdItem.sale_info.sale_price = invItem.SalePrice; llsdItem.sale_info.sale_type = invItem.SaleType; return llsdItem; } } class InventoryCollectionWithDescendents { public InventoryCollection Collection; public int Descendents; } }