From adc9318beec42c25bf864808e5807492715e88e6 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 22 Apr 2022 20:20:08 +0100 Subject: [PATCH] missing files --- .../FetchInventory/FetchLibDescHandler.cs | 361 ++++++++++++++++++ .../Linden/Caps/FetchLibDescModule.cs | 328 ++++++++++++++++ 2 files changed, 689 insertions(+) create mode 100644 OpenSim/Capabilities/Handlers/FetchInventory/FetchLibDescHandler.cs create mode 100644 OpenSim/Region/ClientStack/Linden/Caps/FetchLibDescModule.cs diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchLibDescHandler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchLibDescHandler.cs new file mode 100644 index 0000000000..15f54c23d1 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchLibDescHandler.cs @@ -0,0 +1,361 @@ +/* + * 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.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Capabilities; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; +using OSDArray = OpenMetaverse.StructuredData.OSDArray; + +namespace OpenSim.Capabilities.Handlers +{ + public class FetchLibDescHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static byte[] EmptyResponse = Util.UTF8NBGetbytes("folders"); + private ILibraryService m_LibraryService; + private IScene m_Scene; + + public FetchLibDescHandler(ILibraryService libService, IScene s) + { + m_LibraryService = libService; + m_Scene = s; + } + + public void FetchRequest(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, ExpiringKey BadRequests) + { + //m_log.DebugFormat("[XXX]: FetchLibDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, request); + + List folders = null; + List bad_folders = new List(); + try + { + OSDArray foldersrequested = null; + OSD tmp = OSDParser.DeserializeLLSDXml(httpRequest.InputStream); + httpRequest.InputStream.Dispose(); + + OSDMap map = (OSDMap)tmp; + if(map.TryGetValue("folders", out tmp) && tmp is OSDArray) + foldersrequested = tmp as OSDArray; + + if (foldersrequested == null || foldersrequested.Count == 0) + { + httpResponse.RawBuffer = EmptyResponse; + return; + } + + folders = new List(foldersrequested.Count); + for (int i = 0; i < foldersrequested.Count; i++) + { + OSDMap mfolder = foldersrequested[i] as OSDMap; + UUID id = mfolder["folder_id"].AsUUID(); + if(BadRequests.ContainsKey(id)) + { + bad_folders.Add(id); + } + else + { + LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); + try + { + llsdRequest.folder_id = id; + llsdRequest.owner_id = mfolder["owner_id"].AsUUID(); + llsdRequest.sort_order = mfolder["sort_order"].AsInteger(); + llsdRequest.fetch_folders = mfolder["fetch_folders"].AsBoolean(); + llsdRequest.fetch_items = mfolder["fetch_items"].AsBoolean(); + } + catch (Exception e) + { + m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e.Message); + continue; + } + folders.Add(llsdRequest); + } + } + foldersrequested = null; + map.Clear(); + map = null; + } + catch (Exception e) + { + m_log.ErrorFormat("[FETCH LIB DESC]: fail parsing request: {0}", e.Message); + httpResponse.RawBuffer = EmptyResponse; + return; + } + + if (folders == null || folders.Count == 0) + { + if(bad_folders.Count == 0) + { + httpResponse.RawBuffer = EmptyResponse; + return; + } + + osUTF8 osu = OSUTF8Cached.Acquire(); + osu.AppendASCII("[FETCH LIB DESC HANDLER]: Unable to fetch folders:"); + int limit = 5; + int count = 0; + foreach (UUID bad in bad_folders) + { + if (BadRequests.ContainsKey(bad)) + continue; + osu.Append((byte)' '); + osu.AppendASCII(bad.ToString()); + ++count; + if (--limit < 0) + break; + } + + if(count > 0) + { + if (limit < 0) + osu.AppendASCII(" ..."); + m_log.Warn(osu.ToString()); + } + + osu.Clear(); + + osu.AppendASCII("foldersbad_folders"); + foreach (UUID bad in bad_folders) + { + osu.AppendASCII("folder_id"); + osu.AppendASCII(bad.ToString()); + osu.AppendASCII("errorUnknown"); + } + osu.AppendASCII(""); + httpResponse.RawBuffer = OSUTF8Cached.GetArrayAndRelease(osu); + return; + } + + int total_folders = 0; + int total_items = 0; + + UUID requester = folders[0].owner_id; + + List invcollSet = Fetch(folders, bad_folders, ref total_folders, ref total_items); + //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count); + + int invcollSetCount = 0; + if (invcollSet != null) + invcollSetCount = invcollSet.Count; + + int mem = 8192 + ((256 * invcollSetCount + + 384 * total_folders + + 1024 * total_items + + 128 * bad_folders.Count) & 0x7ffff000); + + osUTF8 lastresponse = LLSDxmlEncode2.Start(mem); + + if (invcollSetCount > 0) + { + lastresponse.AppendASCII("folders"); + int i = 0; + InventoryCollection thiscoll; + for (i = 0; i < invcollSetCount; i++) + { + thiscoll = invcollSet[i]; + invcollSet[i] = null; + + LLSDxmlEncode2.AddMap(lastresponse); + LLSDxmlEncode2.AddElem_folder_id(thiscoll.FolderID, lastresponse); + LLSDxmlEncode2.AddElem_agent_id(thiscoll.OwnerID, lastresponse); + LLSDxmlEncode2.AddElem_owner_id(thiscoll.OwnerID, lastresponse); + LLSDxmlEncode2.AddElem("descendents", thiscoll.Descendents, lastresponse); + LLSDxmlEncode2.AddElem_version(thiscoll.Version, lastresponse); + + if (thiscoll.Folders == null || thiscoll.Folders.Count == 0) + LLSDxmlEncode2.AddEmptyArray("categories", lastresponse); + else + { + LLSDxmlEncode2.AddArray("categories", lastresponse); + foreach (InventoryFolderBase invFolder in thiscoll.Folders) + { + LLSDxmlEncode2.AddMap(lastresponse); + + LLSDxmlEncode2.AddElem_category_id(invFolder.ID, lastresponse); + LLSDxmlEncode2.AddElem_parent_id(invFolder.ParentID, lastresponse); + LLSDxmlEncode2.AddElem_name(invFolder.Name, lastresponse); + LLSDxmlEncode2.AddElem("type_default", invFolder.Type, lastresponse); + LLSDxmlEncode2.AddElem_version( invFolder.Version, lastresponse); + + LLSDxmlEncode2.AddEndMap(lastresponse); + } + LLSDxmlEncode2.AddEndArray(lastresponse); + } + + if (thiscoll.Items == null || thiscoll.Items.Count == 0) + LLSDxmlEncode2.AddEmptyArray("items", lastresponse); + else + { + LLSDxmlEncode2.AddArray("items", lastresponse); + foreach (InventoryItemBase invItem in thiscoll.Items) + { + invItem.ToLLSDxml(lastresponse); + } + + LLSDxmlEncode2.AddEndArray(lastresponse); + } + + + LLSDxmlEncode2.AddEndMap(lastresponse); + invcollSet[i] = null; + } + LLSDxmlEncode2.AddEndArrayAndMap(lastresponse); + thiscoll = null; + } + else + { + lastresponse.AppendASCII("folders"); + } + + if (bad_folders.Count > 0) + { + lastresponse.AppendASCII("bad_folders"); + foreach (UUID bad in bad_folders) + { + BadRequests.Add(bad); + lastresponse.AppendASCII("folder_id"); + lastresponse.AppendASCII(bad.ToString()); + lastresponse.AppendASCII("errorUnknown"); + } + lastresponse.AppendASCII(""); + + StringBuilder sb = osStringBuilderCache.Acquire(); + sb.Append("[WEB FETCH INV DESC HANDLER]: Unable to fetch folders owned by "); + sb.Append(requester.ToString()); + sb.Append(" :"); + int limit = 9; + foreach (UUID bad in bad_folders) + { + sb.Append(" "); + sb.Append(bad.ToString()); + if(--limit < 0) + break; + } + if(limit < 0) + sb.Append(" ..."); + m_log.Warn(osStringBuilderCache.GetStringAndRelease(sb)); + } + + httpResponse.RawBuffer = LLSDxmlEncode2.EndToBytes(lastresponse); + } + + private void AddLibraryFolders(List libFolders, List result, ref int total_folders, ref int total_items) + { + InventoryFolderImpl fold; + if (m_LibraryService == null || m_LibraryService.LibraryRootFolder == null) + return; + + foreach (LLSDFetchInventoryDescendents f in libFolders) + { + if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null) + { + InventoryCollection Collection = new InventoryCollection(); +// ret.Collection.Folders = new List(); + Collection.Folders = fold.RequestListOfFolders(); + Collection.Items = fold.RequestListOfItems(); + Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner; + Collection.FolderID = f.folder_id; + Collection.Version = fold.Version; + + Collection.Descendents = Collection.Items.Count + Collection.Folders.Count; + total_folders += Collection.Folders.Count; + total_items += Collection.Items.Count; + result.Add(Collection); + + //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID); + } + } + } + + private List Fetch(List fetchFolders, List bad_folders, ref int total_folders, ref int total_items) + { + //m_log.DebugFormat( + // "[FETCH LIB DESC HANDLER]: Fetching {0} folders", fetchFolders.Count); + + // FIXME MAYBE: We're not handling sortOrder! + + List result = new List(32); + List libFolders = new List(32); + HashSet libIDs = new HashSet(); + UUID libOwner = m_LibraryService.LibraryRootFolder.Owner; + + // 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) + // send something. + bool doneZeroID = false; + foreach(LLSDFetchInventoryDescendents f in fetchFolders) + { + if (f.folder_id.IsZero()) + { + if(doneZeroID) + continue; + doneZeroID = true; + InventoryCollection Collection = new InventoryCollection() + { + OwnerID = f.owner_id, + Version = 0, + FolderID = f.folder_id, + Descendents = 0 + }; + result.Add(Collection); + continue; + } + if(f.owner_id.Equals(libOwner)) + { + if(libIDs.Contains(f.folder_id)) + continue; + libIDs.Add(f.folder_id); + libFolders.Add(f); + continue; + } + } + + if(libFolders.Count > 0) + { + AddLibraryFolders(libFolders, result, ref total_folders, ref total_items); + } + return result; + } + + private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List bad_folders) + { + bad_folders.Add(freq.folder_id); + return true; + } + } +} diff --git a/OpenSim/Region/ClientStack/Linden/Caps/FetchLibDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/FetchLibDescModule.cs new file mode 100644 index 0000000000..385be6840d --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/FetchLibDescModule.cs @@ -0,0 +1,328 @@ +/* + * 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.Reflection; +using log4net; +using Nini.Config; +using Mono.Addins; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using Caps = OpenSim.Framework.Capabilities.Caps; +using OpenSim.Capabilities.Handlers; +using OpenSim.Framework.Monitoring; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Region.ClientStack.Linden +{ + /// + /// This module implements both WebFetchInventoryDescendents and FetchInventoryDescendents2 capabilities. + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FetchLibDescModule")] + public class FetchLibDescModule : INonSharedRegionModule + { + class APollRequest + { + public PollServiceInventoryEventArgs thepoll; + public UUID reqID; + public OSHttpRequest request; + } + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Control whether requests will be processed asynchronously. + /// + /// + /// Defaults to true. Can currently not be changed once a region has been added to the module. + /// + public bool ProcessQueuedRequestsAsync { get; private set; } + + /// + /// Number of inventory requests processed by this module. + /// + /// + /// It's the PollServiceRequestManager that actually sends completed requests back to the requester. + /// + public static int ProcessedRequestsCount { get; set; } + + public Scene Scene { get; private set; } + + private ILibraryService m_LibraryService; + + private bool m_Enabled; + private ExpiringKey m_badRequests; + + private string m_fetchLibDescendents2Url; + + private static FetchLibDescHandler m_FetchHandler; + + private static ObjectJobEngine m_workerpool = null; + + #region ISharedRegionModule Members + + public FetchLibDescModule() : this(true) {} + + public FetchLibDescModule(bool processQueuedResultsAsync) + { + ProcessQueuedRequestsAsync = processQueuedResultsAsync; + } + + public void Initialise(IConfigSource source) + { + IConfig config = source.Configs["ClientStack.LindenCaps"]; + if (config == null) + return; + + m_fetchLibDescendents2Url = config.GetString("Cap_FetchLibDescendents2", string.Empty); + m_Enabled = m_fetchLibDescendents2Url.Length > 0; + } + + public void AddRegion(Scene s) + { + if (!m_Enabled) + return; + + Scene = s; + } + + public void RemoveRegion(Scene s) + { + if (!m_Enabled) + return; + + Scene.EventManager.OnRegisterCaps -= RegisterCaps; + Scene = null; + } + + public void RegionLoaded(Scene s) + { + if (!m_Enabled) + return; + + m_LibraryService = Scene.LibraryService; + + // We'll reuse the same handler for all requests. + m_FetchHandler = new FetchLibDescHandler(m_LibraryService, Scene); + + Scene.EventManager.OnRegisterCaps += RegisterCaps; + + if(m_badRequests == null) + m_badRequests = new ExpiringKey(30000); + + if (ProcessQueuedRequestsAsync && m_workerpool == null) + m_workerpool = new ObjectJobEngine(DoInventoryRequests, "InventoryWorker", 2000, 2); + } + + public void PostInitialise() + { + } + + public void Close() + { + if (!m_Enabled) + return; + + if (ProcessQueuedRequestsAsync) + { + if (m_workerpool != null) + { + m_workerpool.Dispose(); + m_workerpool = null; + m_badRequests.Dispose(); + m_badRequests = null; + } + } + //m_queue.Dispose(); + } + + public string Name { get { return "WebFetchInvDescModule"; } } + + public Type ReplaceableInterface + { + get { return null; } + } + + #endregion + + private class PollServiceInventoryEventArgs : PollServiceEventArgs + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary responses = new Dictionary(); + private HashSet dropedResponses = new HashSet(); + + private FetchLibDescModule m_module; + + public PollServiceInventoryEventArgs(FetchLibDescModule module, string url, UUID pId) : + base(null, url, null, null, null, null, pId, int.MaxValue) + { + m_module = module; + + HasEvents = (requestID, y) => + { + lock (responses) + return responses.ContainsKey(requestID); + }; + + Drop = (requestID, y) => + { + lock (responses) + { + responses.Remove(requestID); + lock(dropedResponses) + dropedResponses.Add(requestID); + } + }; + + GetEvents = (requestID, y) => + { + lock (responses) + { + try + { + return responses[requestID]; + } + finally + { + responses.Remove(requestID); + } + } + }; + + Request = (requestID, request) => + { + APollRequest reqinfo = new APollRequest(); + reqinfo.thepoll = this; + reqinfo.reqID = requestID; + reqinfo.request = request; + m_workerpool.Enqueue(reqinfo); + return null; + }; + + NoEvents = (x, y) => + { + Hashtable response = new Hashtable(); + response["int_response_code"] = 500; + response["str_response_string"] = "Script timeout"; + response["content_type"] = "text/plain"; + response["keepalive"] = false; + + return response; + }; + } + + public void Process(APollRequest requestinfo) + { + if(m_module == null || m_module.Scene == null || m_module.Scene.ShuttingDown) + return; + + UUID requestID = requestinfo.reqID; + + lock(responses) + { + lock(dropedResponses) + { + if(dropedResponses.Contains(requestID)) + { + dropedResponses.Remove(requestID); + return; + } + } + } + + OSHttpResponse osresponse = new OSHttpResponse(requestinfo.request); + m_FetchHandler.FetchRequest(requestinfo.request, osresponse, m_module.m_badRequests); + requestinfo.request.InputStream.Dispose(); + + lock (responses) + { + lock(dropedResponses) + { + if(dropedResponses.Contains(requestID)) + { + dropedResponses.Remove(requestID); + ProcessedRequestsCount++; + return; + } + } + + Hashtable response = new Hashtable(); + response["h"] = osresponse; + responses[requestID] = response; + } + ProcessedRequestsCount++; + } + } + + private void RegisterCaps(UUID agentID, Caps caps) + { + RegisterFetchLibDescendentsCap(agentID, caps, "FetchLibDescendents2", m_fetchLibDescendents2Url); + } + + private void RegisterFetchLibDescendentsCap(UUID agentID, Caps caps, string capName, string url) + { + string capUrl; + + // handled by the simulator + if (url == "localhost") + { + capUrl = "/" + UUID.Random(); + + // Register this as a poll service + PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID); + //args.Type = PollServiceEventArgs.EventType.Inventory; + + caps.RegisterPollHandler(capName, args); + } + // external handler + else + { + capUrl = url; + IExternalCapsModule handler = Scene.RequestModuleInterface(); + if (handler != null) + handler.RegisterExternalUserCapsHandler(agentID, caps, capName, capUrl); + else + caps.RegisterHandler(capName, capUrl); + } + } + + private static void DoInventoryRequests(object o) + { + APollRequest poolreq = o as APollRequest; + if (poolreq != null && poolreq.thepoll != null) + poolreq.thepoll.Process(poolreq); + } + } +}