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("");
+ 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("");
+ 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);
+ }
+ }
+}