WebFetchInventoryDescendents working. Tested with robust.
parent
2d403d5b18
commit
883f21dd02
|
@ -106,6 +106,10 @@ namespace OpenSim.Framework.Capabilities
|
||||||
{
|
{
|
||||||
get { return m_capsHandlers; }
|
get { return m_capsHandlers; }
|
||||||
}
|
}
|
||||||
|
public Dictionary<string, string> ExternalCapsHandlers
|
||||||
|
{
|
||||||
|
get { return m_externalCapsHandlers; }
|
||||||
|
}
|
||||||
|
|
||||||
public Caps(IHttpServer httpServer, string httpListen, uint httpPort, string capsPath,
|
public Caps(IHttpServer httpServer, string httpListen, uint httpPort, string capsPath,
|
||||||
UUID agent, string regionName)
|
UUID agent, string regionName)
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* 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 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 WebFetchInvDescHandler
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log =
|
||||||
|
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
private IInventoryService m_InventoryService;
|
||||||
|
private ILibraryService m_LibraryService;
|
||||||
|
private object m_fetchLock = new Object();
|
||||||
|
|
||||||
|
public WebFetchInvDescHandler(IInventoryService invService, ILibraryService libService)
|
||||||
|
{
|
||||||
|
m_InventoryService = invService;
|
||||||
|
m_LibraryService = libService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FetchInventoryDescendentsRequest(string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
|
||||||
|
{
|
||||||
|
// 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("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
|
||||||
|
|
||||||
|
// another hack <integer>1</integer> results in a
|
||||||
|
// System.ArgumentException: Object type System.Int32 cannot
|
||||||
|
// be converted to target type: System.Boolean
|
||||||
|
//
|
||||||
|
request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
|
||||||
|
request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
|
||||||
|
|
||||||
|
Hashtable hash = new Hashtable();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
|
||||||
|
}
|
||||||
|
catch (LLSD.LLSDParseException pe)
|
||||||
|
{
|
||||||
|
m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message);
|
||||||
|
m_log.Error("Request: " + request.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList foldersrequested = (ArrayList)hash["folders"];
|
||||||
|
|
||||||
|
string response = "";
|
||||||
|
lock (m_fetchLock)
|
||||||
|
{
|
||||||
|
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("[CAPS]: caught exception doing OSD deserialize" + e);
|
||||||
|
}
|
||||||
|
LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
|
||||||
|
|
||||||
|
inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
|
||||||
|
inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
|
||||||
|
inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
|
||||||
|
|
||||||
|
response += inventoryitemstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (response.Length == 0)
|
||||||
|
{
|
||||||
|
// Ter-guess: If requests fail a lot, the client seems to stop requesting descendants.
|
||||||
|
// Therefore, I'm concluding that the client only has so many threads available to do requests
|
||||||
|
// and when a thread stalls.. is stays stalled.
|
||||||
|
// Therefore we need to return something valid
|
||||||
|
response = "<llsd><map><key>folders</key><array /></map></llsd>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
|
||||||
|
}
|
||||||
|
|
||||||
|
//m_log.DebugFormat("[CAPS]: Replying to CAPS fetch inventory request with following xml");
|
||||||
|
//m_log.Debug("[CAPS] "+response);
|
||||||
|
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct an LLSD reply packet to a CAPS inventory request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invFetch"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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<InventoryFolderBase>();
|
||||||
|
inv.Items = new List<InventoryItemBase>();
|
||||||
|
int version = 0;
|
||||||
|
|
||||||
|
inv = Fetch(invFetch.owner_id, invFetch.folder_id, invFetch.owner_id, invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version);
|
||||||
|
|
||||||
|
if (inv.Folders != null)
|
||||||
|
{
|
||||||
|
foreach (InventoryFolderBase invFolder in inv.Folders)
|
||||||
|
{
|
||||||
|
contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inv.Items != null)
|
||||||
|
{
|
||||||
|
foreach (InventoryItemBase invItem in inv.Items)
|
||||||
|
{
|
||||||
|
contents.items.Array.Add(ConvertInventoryItem(invItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.descendents = contents.items.Array.Count + contents.categories.Array.Count;
|
||||||
|
contents.version = version;
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InventoryCollection Fetch(UUID agentID, UUID folderID, UUID ownerID,
|
||||||
|
bool fetchFolders, bool fetchItems, int sortOrder, out int version)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[WEBFETCHINVENTORYDESCENDANTS]: Fetching folders ({0}), items ({1}) from {2} for agent {3}",
|
||||||
|
fetchFolders, fetchItems, folderID, agentID);
|
||||||
|
|
||||||
|
version = 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<InventoryFolderBase>();
|
||||||
|
ret.Items = fold.RequestListOfItems();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryCollection contents = new InventoryCollection();
|
||||||
|
|
||||||
|
if (folderID != UUID.Zero)
|
||||||
|
{
|
||||||
|
contents = m_InventoryService.GetFolderContent(agentID, folderID);
|
||||||
|
InventoryFolderBase containingFolder = new InventoryFolderBase();
|
||||||
|
containingFolder.ID = folderID;
|
||||||
|
containingFolder.Owner = agentID;
|
||||||
|
containingFolder = m_InventoryService.GetFolder(containingFolder);
|
||||||
|
if (containingFolder != null)
|
||||||
|
version = containingFolder.Version;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Lost itemsm don't really need a version
|
||||||
|
version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Convert an internal inventory folder object into an LLSD object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invFolder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder)
|
||||||
|
{
|
||||||
|
LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder();
|
||||||
|
llsdFolder.folder_id = invFolder.ID;
|
||||||
|
llsdFolder.parent_id = invFolder.ParentID;
|
||||||
|
llsdFolder.name = invFolder.Name;
|
||||||
|
if (invFolder.Type < 0 || invFolder.Type >= TaskInventoryItem.Types.Length)
|
||||||
|
llsdFolder.type = "-1";
|
||||||
|
else
|
||||||
|
llsdFolder.type = TaskInventoryItem.Types[invFolder.Type];
|
||||||
|
llsdFolder.preferred_type = "-1";
|
||||||
|
|
||||||
|
return llsdFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert an internal inventory item object into an LLSD object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invItem"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO reevaluate after upgrade to libomv >= r2566. Probably should use UtilsConversions.
|
||||||
|
llsdItem.type = TaskInventoryItem.Types[invItem.AssetType];
|
||||||
|
llsdItem.inv_type = TaskInventoryItem.InvTypes[invItem.InvType];
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("[CAPS]: Problem setting asset {0} inventory {1} types while converting inventory item {2}: {3}", invItem.AssetType, invItem.InvType, invItem.Name, e.Message);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
switch (invItem.SaleType)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
llsdItem.sale_info.sale_type = "not";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
llsdItem.sale_info.sale_type = "original";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
llsdItem.sale_info.sale_type = "copy";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
llsdItem.sale_info.sale_type = "contents";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return llsdItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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 Nini.Config;
|
||||||
|
using OpenSim.Server.Base;
|
||||||
|
using OpenSim.Services.Interfaces;
|
||||||
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
|
using OpenSim.Server.Handlers.Base;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Capabilities.Handlers
|
||||||
|
{
|
||||||
|
public class WebFetchInvDescServerConnector : ServiceConnector
|
||||||
|
{
|
||||||
|
private IInventoryService m_InventoryService;
|
||||||
|
private ILibraryService m_LibraryService;
|
||||||
|
private string m_ConfigName = "CapsService";
|
||||||
|
|
||||||
|
public WebFetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) :
|
||||||
|
base(config, server, configName)
|
||||||
|
{
|
||||||
|
if (configName != String.Empty)
|
||||||
|
m_ConfigName = configName;
|
||||||
|
|
||||||
|
IConfig serverConfig = config.Configs[m_ConfigName];
|
||||||
|
if (serverConfig == null)
|
||||||
|
throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName));
|
||||||
|
|
||||||
|
string invService = serverConfig.GetString("InventoryService", String.Empty);
|
||||||
|
|
||||||
|
if (invService == String.Empty)
|
||||||
|
throw new Exception("No InventoryService in config file");
|
||||||
|
|
||||||
|
Object[] args = new Object[] { config };
|
||||||
|
m_InventoryService =
|
||||||
|
ServerUtils.LoadPlugin<IInventoryService>(invService, args);
|
||||||
|
|
||||||
|
if (m_InventoryService == null)
|
||||||
|
throw new Exception(String.Format("Failed to load InventoryService from {0}; config is {1}", invService, m_ConfigName));
|
||||||
|
|
||||||
|
string libService = serverConfig.GetString("LibraryService", String.Empty);
|
||||||
|
m_LibraryService =
|
||||||
|
ServerUtils.LoadPlugin<ILibraryService>(libService, args);
|
||||||
|
|
||||||
|
WebFetchInvDescHandler webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
|
||||||
|
IRequestHandler reqHandler = new RestStreamHandler("POST", "/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/, webFetchHandler.FetchInventoryDescendentsRequest);
|
||||||
|
server.AddStreamHandler(reqHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,7 +81,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
private IAssetService m_assetService;
|
private IAssetService m_assetService;
|
||||||
private bool m_dumpAssetsToFile;
|
private bool m_dumpAssetsToFile;
|
||||||
private string m_regionName;
|
private string m_regionName;
|
||||||
private object m_fetchLock = new Object();
|
|
||||||
|
|
||||||
public BunchOfCaps(Scene scene, Caps caps)
|
public BunchOfCaps(Scene scene, Caps caps)
|
||||||
{
|
{
|
||||||
|
@ -211,8 +210,12 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: Add the external too
|
Hashtable caps = m_HostCapsObj.CapsHandlers.CapsDetails;
|
||||||
string result = LLSDHelpers.SerialiseLLSDReply(m_HostCapsObj.CapsHandlers.CapsDetails);
|
// Add the external too
|
||||||
|
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
|
||||||
|
caps[kvp.Key] = kvp.Value;
|
||||||
|
|
||||||
|
string result = LLSDHelpers.SerialiseLLSDReply(caps);
|
||||||
|
|
||||||
//m_log.DebugFormat("[CAPS] CapsRequest {0}", result);
|
//m_log.DebugFormat("[CAPS] CapsRequest {0}", result);
|
||||||
|
|
||||||
|
@ -505,283 +508,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Processes a fetch inventory request and sends the reply
|
|
||||||
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <param name="param"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
// Request is like:
|
|
||||||
//<llsd>
|
|
||||||
// <map><key>folders</key>
|
|
||||||
// <array>
|
|
||||||
// <map>
|
|
||||||
// <key>fetch-folders</key><boolean>1</boolean><key>fetch-items</key><boolean>1</boolean><key>folder-id</key><uuid>8e1e3a30-b9bf-11dc-95ff-0800200c9a66</uuid><key>owner-id</key><uuid>11111111-1111-0000-0000-000100bba000</uuid><key>sort-order</key><integer>1</integer>
|
|
||||||
// </map>
|
|
||||||
// </array>
|
|
||||||
// </map>
|
|
||||||
//</llsd>
|
|
||||||
//
|
|
||||||
// multiple fetch-folder maps are allowed within the larger folders map.
|
|
||||||
public string FetchInventoryRequest(string request, string path, string param)
|
|
||||||
{
|
|
||||||
// string unmodifiedRequest = request.ToString();
|
|
||||||
|
|
||||||
//m_log.DebugFormat("[AGENT INVENTORY]: Received CAPS fetch inventory request {0}", unmodifiedRequest);
|
|
||||||
m_log.Debug("[CAPS]: Inventory Request in region: " + m_regionName);
|
|
||||||
|
|
||||||
Hashtable hash = new Hashtable();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
|
|
||||||
}
|
|
||||||
catch (LLSD.LLSDParseException pe)
|
|
||||||
{
|
|
||||||
m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message);
|
|
||||||
m_log.Error("Request: " + request.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList foldersrequested = (ArrayList)hash["folders"];
|
|
||||||
|
|
||||||
string response = "";
|
|
||||||
|
|
||||||
for (int i = 0; i < foldersrequested.Count; i++)
|
|
||||||
{
|
|
||||||
string inventoryitemstr = "";
|
|
||||||
Hashtable inventoryhash = (Hashtable)foldersrequested[i];
|
|
||||||
|
|
||||||
LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
|
|
||||||
LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
|
|
||||||
LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
|
|
||||||
|
|
||||||
inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
|
|
||||||
inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
|
|
||||||
inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
|
|
||||||
|
|
||||||
response += inventoryitemstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.Length == 0)
|
|
||||||
{
|
|
||||||
// Ter-guess: If requests fail a lot, the client seems to stop requesting descendants.
|
|
||||||
// Therefore, I'm concluding that the client only has so many threads available to do requests
|
|
||||||
// and when a thread stalls.. is stays stalled.
|
|
||||||
// Therefore we need to return something valid
|
|
||||||
response = "<llsd><map><key>folders</key><array /></map></llsd>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
|
|
||||||
}
|
|
||||||
|
|
||||||
//m_log.DebugFormat("[AGENT INVENTORY]: Replying to CAPS fetch inventory request with following xml");
|
|
||||||
//m_log.Debug(Util.GetFormattedXml(response));
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FetchInventoryDescendentsRequest(string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// 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("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
|
|
||||||
|
|
||||||
// another hack <integer>1</integer> results in a
|
|
||||||
// System.ArgumentException: Object type System.Int32 cannot
|
|
||||||
// be converted to target type: System.Boolean
|
|
||||||
//
|
|
||||||
request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
|
|
||||||
request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
|
|
||||||
|
|
||||||
Hashtable hash = new Hashtable();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
|
|
||||||
}
|
|
||||||
catch (LLSD.LLSDParseException pe)
|
|
||||||
{
|
|
||||||
m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message);
|
|
||||||
m_log.Error("Request: " + request.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList foldersrequested = (ArrayList)hash["folders"];
|
|
||||||
|
|
||||||
string response = "";
|
|
||||||
lock (m_fetchLock)
|
|
||||||
{
|
|
||||||
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("[CAPS]: caught exception doing OSD deserialize" + e);
|
|
||||||
}
|
|
||||||
LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
|
|
||||||
|
|
||||||
inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
|
|
||||||
inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
|
|
||||||
inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
|
|
||||||
|
|
||||||
response += inventoryitemstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (response.Length == 0)
|
|
||||||
{
|
|
||||||
// Ter-guess: If requests fail a lot, the client seems to stop requesting descendants.
|
|
||||||
// Therefore, I'm concluding that the client only has so many threads available to do requests
|
|
||||||
// and when a thread stalls.. is stays stalled.
|
|
||||||
// Therefore we need to return something valid
|
|
||||||
response = "<llsd><map><key>folders</key><array /></map></llsd>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
|
|
||||||
}
|
|
||||||
|
|
||||||
//m_log.DebugFormat("[CAPS]: Replying to CAPS fetch inventory request with following xml");
|
|
||||||
//m_log.Debug("[CAPS] "+response);
|
|
||||||
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Construct an LLSD reply packet to a CAPS inventory request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invFetch"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch)
|
|
||||||
{
|
|
||||||
LLSDInventoryDescendents reply = new LLSDInventoryDescendents();
|
|
||||||
LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents();
|
|
||||||
contents.agent_id = m_HostCapsObj.AgentID;
|
|
||||||
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<InventoryFolderBase>();
|
|
||||||
inv.Items = new List<InventoryItemBase>();
|
|
||||||
int version = 0;
|
|
||||||
if (CAPSFetchInventoryDescendents != null)
|
|
||||||
{
|
|
||||||
inv = CAPSFetchInventoryDescendents(m_HostCapsObj.AgentID, invFetch.folder_id, invFetch.owner_id, invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inv.Folders != null)
|
|
||||||
{
|
|
||||||
foreach (InventoryFolderBase invFolder in inv.Folders)
|
|
||||||
{
|
|
||||||
contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inv.Items != null)
|
|
||||||
{
|
|
||||||
foreach (InventoryItemBase invItem in inv.Items)
|
|
||||||
{
|
|
||||||
contents.items.Array.Add(ConvertInventoryItem(invItem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contents.descendents = contents.items.Array.Count + contents.categories.Array.Count;
|
|
||||||
contents.version = version;
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert an internal inventory folder object into an LLSD object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invFolder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder)
|
|
||||||
{
|
|
||||||
LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder();
|
|
||||||
llsdFolder.folder_id = invFolder.ID;
|
|
||||||
llsdFolder.parent_id = invFolder.ParentID;
|
|
||||||
llsdFolder.name = invFolder.Name;
|
|
||||||
if (invFolder.Type < 0 || invFolder.Type >= TaskInventoryItem.Types.Length)
|
|
||||||
llsdFolder.type = "-1";
|
|
||||||
else
|
|
||||||
llsdFolder.type = TaskInventoryItem.Types[invFolder.Type];
|
|
||||||
llsdFolder.preferred_type = "-1";
|
|
||||||
|
|
||||||
return llsdFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert an internal inventory item object into an LLSD object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invItem"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO reevaluate after upgrade to libomv >= r2566. Probably should use UtilsConversions.
|
|
||||||
llsdItem.type = TaskInventoryItem.Types[invItem.AssetType];
|
|
||||||
llsdItem.inv_type = TaskInventoryItem.InvTypes[invItem.InvType];
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
m_log.Error("[CAPS]: Problem setting asset/inventory type while converting inventory item " + invItem.Name + " to LLSD:", e);
|
|
||||||
}
|
|
||||||
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 = m_HostCapsObj.AgentID;
|
|
||||||
llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
|
|
||||||
llsdItem.sale_info = new LLSDSaleInfo();
|
|
||||||
llsdItem.sale_info.sale_price = invItem.SalePrice;
|
|
||||||
switch (invItem.SaleType)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
llsdItem.sale_info.sale_type = "not";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
llsdItem.sale_info.sale_type = "original";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
llsdItem.sale_info.sale_type = "copy";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
llsdItem.sale_info.sale_type = "contents";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return llsdItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* 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.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;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.ClientStack.Linden
|
||||||
|
{
|
||||||
|
|
||||||
|
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
|
||||||
|
public class WebFetchInvDescModule : INonSharedRegionModule
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log =
|
||||||
|
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
private Scene m_scene;
|
||||||
|
|
||||||
|
private IInventoryService m_InventoryService;
|
||||||
|
private ILibraryService m_LibraryService;
|
||||||
|
private bool m_Enabled = false;
|
||||||
|
private string m_URL;
|
||||||
|
|
||||||
|
#region ISharedRegionModule Members
|
||||||
|
|
||||||
|
public void Initialise(IConfigSource source)
|
||||||
|
{
|
||||||
|
IConfig config = source.Configs["ClientStack.LindenCaps"];
|
||||||
|
if (config == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_URL = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
|
||||||
|
// Cap doesn't exist
|
||||||
|
if (m_URL != string.Empty)
|
||||||
|
m_Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRegion(Scene s)
|
||||||
|
{
|
||||||
|
if (!m_Enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_scene = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveRegion(Scene s)
|
||||||
|
{
|
||||||
|
m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
|
||||||
|
m_scene = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegionLoaded(Scene s)
|
||||||
|
{
|
||||||
|
if (!m_Enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_InventoryService = m_scene.InventoryService; ;
|
||||||
|
m_LibraryService = m_scene.LibraryService;
|
||||||
|
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PostInitialise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close() { }
|
||||||
|
|
||||||
|
public string Name { get { return "WebFetchInvDescModule"; } }
|
||||||
|
|
||||||
|
public Type ReplaceableInterface
|
||||||
|
{
|
||||||
|
get { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void RegisterCaps(UUID agentID, Caps caps)
|
||||||
|
{
|
||||||
|
UUID capID = UUID.Random();
|
||||||
|
|
||||||
|
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
|
||||||
|
if (m_URL == "localhost")
|
||||||
|
{
|
||||||
|
m_log.InfoFormat("[WEBFETCHINVENTORYDESCENDANTS]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
|
||||||
|
WebFetchInvDescHandler webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
|
||||||
|
IRequestHandler reqHandler = new RestStreamHandler("POST", "/CAPS/" + UUID.Random(), webFetchHandler.FetchInventoryDescendentsRequest);
|
||||||
|
caps.RegisterHandler("WebFetchInventoryDescendents", reqHandler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.InfoFormat("[WEBFETCHINVENTORYDESCENDANTS]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
||||||
|
caps.RegisterHandler("WebFetchInventoryDescendents", m_URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue