/* * 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.Collections.Specialized; using System.Reflection; using log4net; using Mono.Addins; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Services.Connectors.SimianGrid { /// /// Permissions bitflags /// /* [Flags] public enum PermissionMask : uint { None = 0, Transfer = 1 << 13, Modify = 1 << 14, Copy = 1 << 15, Move = 1 << 19, Damage = 1 << 20, All = 0x7FFFFFFF } */ /// /// Connects avatar inventories to the SimianGrid backend /// [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianInventoryServiceConnector")] public class SimianInventoryServiceConnector : IInventoryService, ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); private string m_serverUrl = String.Empty; private string m_userServerUrl = String.Empty; // private object m_gestureSyncRoot = new object(); private bool m_Enabled = false; private const double CACHE_EXPIRATION_SECONDS = 20.0; private static ExpiringCache m_ItemCache; #region ISharedRegionModule public Type ReplaceableInterface { get { return null; } } public void RegionLoaded(Scene scene) { } public void PostInitialise() { } public void Close() { } public SimianInventoryServiceConnector() { } public string Name { get { return "SimianInventoryServiceConnector"; } } public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } #endregion ISharedRegionModule public SimianInventoryServiceConnector(IConfigSource source) { CommonInit(source); } public SimianInventoryServiceConnector(string url) { if (!url.EndsWith("/") && !url.EndsWith("=")) url = url + '/'; m_serverUrl = url; if (m_ItemCache == null) m_ItemCache = new ExpiringCache(); } public void Initialise(IConfigSource source) { IConfig moduleConfig = source.Configs["Modules"]; if (moduleConfig != null) { string name = moduleConfig.GetString("InventoryServices", ""); if (name == Name) CommonInit(source); } } private void CommonInit(IConfigSource source) { IConfig gridConfig = source.Configs["InventoryService"]; if (gridConfig != null) { string serviceUrl = gridConfig.GetString("InventoryServerURI"); if (!String.IsNullOrEmpty(serviceUrl)) { if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) serviceUrl = serviceUrl + '/'; m_serverUrl = serviceUrl; gridConfig = source.Configs["UserAccountService"]; if (gridConfig != null) { serviceUrl = gridConfig.GetString("UserAccountServerURI"); if (!String.IsNullOrEmpty(serviceUrl)) { m_userServerUrl = serviceUrl; m_Enabled = true; if (m_ItemCache == null) m_ItemCache = new ExpiringCache(); } } } } if (String.IsNullOrEmpty(m_serverUrl)) m_log.Info("[SIMIAN INVENTORY CONNECTOR]: No InventoryServerURI specified, disabling connector"); else if (String.IsNullOrEmpty(m_userServerUrl)) m_log.Info("[SIMIAN INVENTORY CONNECTOR]: No UserAccountServerURI specified, disabling connector"); } /// /// Create the entire inventory for a given user /// /// /// public bool CreateUserInventory(UUID userID) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddInventory" }, { "OwnerID", userID.ToString() } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Inventory creation for " + userID + " failed: " + response["Message"].AsString()); return success; } /// /// Gets the skeleton of the inventory -- folders only /// /// /// public List GetInventorySkeleton(UUID userID) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", userID.ToString() }, { "OwnerID", userID.ToString() }, { "IncludeFolders", "1" }, { "IncludeItems", "0" }, { "ChildrenOnly", "0" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { OSDArray items = (OSDArray)response["Items"]; return GetFoldersFromResponse(items, userID, true); } else { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to retrieve inventory skeleton for " + userID + ": " + response["Message"].AsString()); return new List(0); } } /// /// Retrieve the root inventory folder for the given user. /// /// /// null if no root folder was found public InventoryFolderBase GetRootFolder(UUID userID) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", userID.ToString() }, { "OwnerID", userID.ToString() }, { "IncludeFolders", "1" }, { "IncludeItems", "0" }, { "ChildrenOnly", "1" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { OSDArray items = (OSDArray)response["Items"]; List folders = GetFoldersFromResponse(items, userID, true); if (folders.Count > 0) return folders[0]; } return null; } /// /// Gets the user folder for the given folder-type /// /// /// /// public InventoryFolderBase GetFolderForType(UUID userID, FolderType type) { string contentType = SLUtil.SLAssetTypeToContentType((int)type); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetFolderForType" }, { "ContentType", contentType }, { "OwnerID", userID.ToString() } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Folder"] is OSDMap) { OSDMap folder = (OSDMap)response["Folder"]; return new InventoryFolderBase( folder["ID"].AsUUID(), folder["Name"].AsString(), folder["OwnerID"].AsUUID(), (short)SLUtil.ContentTypeToSLAssetType(folder["ContentType"].AsString()), folder["ParentID"].AsUUID(), (ushort)folder["Version"].AsInteger() ); } else { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Default folder not found for content type " + contentType + ": " + response["Message"].AsString()); return GetRootFolder(userID); } } /// /// Get an item, given by its UUID /// /// /// public InventoryItemBase GetItem(UUID principalID, UUID itemID) { InventoryItemBase retrieved = null; if (m_ItemCache.TryGetValue(itemID, out retrieved)) return retrieved; NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", itemID.ToString() }, { "OwnerID", principalID.ToString() }, { "IncludeFolders", "1" }, { "IncludeItems", "1" }, { "ChildrenOnly", "1" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { List items = GetItemsFromResponse((OSDArray)response["Items"]); if (items.Count > 0) { // The requested item should be the first in this list, but loop through // and sanity check just in case for (int i = 0; i < items.Count; i++) { if (items[i].ID == itemID) { retrieved = items[i]; m_ItemCache.AddOrUpdate(itemID, retrieved, CACHE_EXPIRATION_SECONDS); return retrieved; } } } } m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Item " + itemID + " owned by " + principalID + " not found"); return null; } public InventoryItemBase[] GetMultipleItems(UUID principalID, UUID[] itemIDs) { InventoryItemBase[] result = new InventoryItemBase[itemIDs.Length]; int i = 0; foreach (UUID id in itemIDs) result[i++] = GetItem(principalID, id); return result; } /// /// Get a folder, given by its UUID /// /// /// public InventoryFolderBase GetFolder(UUID principalID, UUID folderID) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", folderID.ToString() }, { "OwnerID", principalID.ToString() }, { "IncludeFolders", "1" }, { "IncludeItems", "0" }, { "ChildrenOnly", "1" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { OSDArray items = (OSDArray)response["Items"]; List folders = GetFoldersFromResponse(items, folderID, true); if (folders.Count > 0) return folders[0]; } return null; } /// /// Gets everything (folders and items) inside a folder /// /// /// /// public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { InventoryCollection inventory = new InventoryCollection(); inventory.OwnerID = userID; NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", folderID.ToString() }, { "OwnerID", userID.ToString() }, { "IncludeFolders", "1" }, { "IncludeItems", "1" }, { "ChildrenOnly", "1" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { OSDArray items = (OSDArray)response["Items"]; inventory.Folders = GetFoldersFromResponse(items, folderID, false); inventory.Items = GetItemsFromResponse(items); } else { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error fetching folder " + folderID + " content for " + userID + ": " + response["Message"].AsString()); inventory.Folders = new List(0); inventory.Items = new List(0); } return inventory; } public virtual InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderIDs) { InventoryCollection[] invColl = new InventoryCollection[folderIDs.Length]; int i = 0; foreach (UUID fid in folderIDs) { invColl[i++] = GetFolderContent(principalID, fid); } return invColl; } /// /// Gets the items inside a folder /// /// /// /// public List GetFolderItems(UUID userID, UUID folderID) { InventoryCollection inventory = new InventoryCollection(); inventory.OwnerID = userID; NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetInventoryNode" }, { "ItemID", folderID.ToString() }, { "OwnerID", userID.ToString() }, { "IncludeFolders", "0" }, { "IncludeItems", "1" }, { "ChildrenOnly", "1" } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["Items"] is OSDArray) { OSDArray items = (OSDArray)response["Items"]; return GetItemsFromResponse(items); } else { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error fetching folder " + folderID + " for " + userID + ": " + response["Message"].AsString()); return new List(0); } } /// /// Add a new folder to the user's inventory /// /// /// true if the folder was successfully added public bool AddFolder(InventoryFolderBase folder) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddInventoryFolder" }, { "FolderID", folder.ID.ToString() }, { "ParentID", folder.ParentID.ToString() }, { "OwnerID", folder.Owner.ToString() }, { "Name", folder.Name }, { "ContentType", SLUtil.SLAssetTypeToContentType(folder.Type) } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error creating folder " + folder.Name + " for " + folder.Owner + ": " + response["Message"].AsString()); } return success; } /// /// Update a folder in the user's inventory /// /// /// true if the folder was successfully updated public bool UpdateFolder(InventoryFolderBase folder) { return AddFolder(folder); } /// /// Move an inventory folder to a new location /// /// A folder containing the details of the new location /// true if the folder was successfully moved public bool MoveFolder(InventoryFolderBase folder) { return AddFolder(folder); } /// /// Delete an item from the user's inventory /// /// /// true if the item was successfully deleted //bool DeleteItem(InventoryItemBase item); public bool DeleteFolders(UUID userID, List folderIDs) { return DeleteItems(userID, folderIDs); } /// /// Delete an item from the user's inventory /// /// /// true if the item was successfully deleted public bool DeleteItems(UUID userID, List itemIDs) { // TODO: RemoveInventoryNode should be replaced with RemoveInventoryNodes bool allSuccess = true; for (int i = 0; i < itemIDs.Count; i++) { UUID itemID = itemIDs[i]; NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "RemoveInventoryNode" }, { "OwnerID", userID.ToString() }, { "ItemID", itemID.ToString() } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error removing item " + itemID + " for " + userID + ": " + response["Message"].AsString()); allSuccess = false; } } return allSuccess; } /// /// Purge an inventory folder of all its items and subfolders. /// /// /// true if the folder was successfully purged public bool PurgeFolder(InventoryFolderBase folder) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "PurgeInventoryFolder" }, { "OwnerID", folder.Owner.ToString() }, { "FolderID", folder.ID.ToString() } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error purging folder " + folder.ID + " for " + folder.Owner + ": " + response["Message"].AsString()); } return success; } /// /// Add a new item to the user's inventory /// /// /// true if the item was successfully added public bool AddItem(InventoryItemBase item) { // A folder of UUID.Zero means we need to find the most appropriate home for this item if (item.Folder == UUID.Zero) { InventoryFolderBase folder = null; if (Enum.IsDefined(typeof(FolderType), (sbyte)item.AssetType)) folder = GetFolderForType(item.Owner, (FolderType)item.AssetType); if (folder != null && folder.ID != UUID.Zero) item.Folder = folder.ID; else item.Folder = item.Owner; // Root folder } if ((AssetType)item.AssetType == AssetType.Gesture) UpdateGesture(item.Owner, item.ID, item.Flags == 1); if (item.BasePermissions == 0) m_log.WarnFormat("[SIMIAN INVENTORY CONNECTOR]: Adding inventory item {0} ({1}) with no base permissions", item.Name, item.ID); OSDMap permissions = new OSDMap { { "BaseMask", OSD.FromInteger(item.BasePermissions) }, { "EveryoneMask", OSD.FromInteger(item.EveryOnePermissions) }, { "GroupMask", OSD.FromInteger(item.GroupPermissions) }, { "NextOwnerMask", OSD.FromInteger(item.NextPermissions) }, { "OwnerMask", OSD.FromInteger(item.CurrentPermissions) } }; OSDMap extraData = new OSDMap() { { "Flags", OSD.FromInteger(item.Flags) }, { "GroupID", OSD.FromUUID(item.GroupID) }, { "GroupOwned", OSD.FromBoolean(item.GroupOwned) }, { "SalePrice", OSD.FromInteger(item.SalePrice) }, { "SaleType", OSD.FromInteger(item.SaleType) }, { "Permissions", permissions } }; // Add different asset type only if it differs from inventory type // (needed for links) string invContentType = SLUtil.SLInvTypeToContentType(item.InvType); string assetContentType = SLUtil.SLAssetTypeToContentType(item.AssetType); if (invContentType != assetContentType) extraData["LinkedItemType"] = OSD.FromString(assetContentType); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddInventoryItem" }, { "ItemID", item.ID.ToString() }, { "AssetID", item.AssetID.ToString() }, { "ParentID", item.Folder.ToString() }, { "OwnerID", item.Owner.ToString() }, { "Name", item.Name }, { "Description", item.Description }, { "CreatorID", item.CreatorId }, { "CreatorData", item.CreatorData }, { "ContentType", invContentType }, { "ExtraData", OSDParser.SerializeJsonString(extraData) } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error creating item " + item.Name + " for " + item.Owner + ": " + response["Message"].AsString()); } return success; } /// /// Update an item in the user's inventory /// /// /// true if the item was successfully updated public bool UpdateItem(InventoryItemBase item) { if (item.AssetID != UUID.Zero) { return AddItem(item); } else { // This is actually a folder update InventoryFolderBase folder = new InventoryFolderBase(item.ID, item.Name, item.Owner, (short)item.AssetType, item.Folder, 0); return UpdateFolder(folder); } } public bool MoveItems(UUID ownerID, List items) { bool success = true; while (items.Count > 0) { List currentItems = new List(); UUID destFolderID = items[0].Folder; // Find all of the items being moved to the current destination folder for (int i = 0; i < items.Count; i++) { InventoryItemBase item = items[i]; if (item.Folder == destFolderID) currentItems.Add(item); } // Do the inventory move for the current items success &= MoveItems(ownerID, items, destFolderID); // Remove the processed items from the list for (int i = 0; i < currentItems.Count; i++) items.Remove(currentItems[i]); } return success; } /// /// Does the given user have an inventory structure? /// /// /// public bool HasInventoryForUser(UUID userID) { return GetRootFolder(userID) != null; } /// /// Get the active gestures of the agent. /// /// /// public List GetActiveGestures(UUID userID) { OSDArray items = FetchGestures(userID); string[] itemIDs = new string[items.Count]; for (int i = 0; i < items.Count; i++) itemIDs[i] = items[i].AsUUID().ToString(); // NameValueCollection requestArgs = new NameValueCollection // { // { "RequestMethod", "GetInventoryNodes" }, // { "OwnerID", userID.ToString() }, // { "Items", String.Join(",", itemIDs) } // }; // FIXME: Implement this in SimianGrid return new List(0); } /// /// Get the union of permissions of all inventory items /// that hold the given assetID. /// /// /// /// The permissions or 0 if no such asset is found in /// the user's inventory public int GetAssetPermissions(UUID userID, UUID assetID) { // NameValueCollection requestArgs = new NameValueCollection // { // { "RequestMethod", "GetInventoryNodes" }, // { "OwnerID", userID.ToString() }, // { "AssetID", assetID.ToString() } // }; // FIXME: Implement this in SimianGrid return (int)PermissionMask.All; } private List GetFoldersFromResponse(OSDArray items, UUID baseFolder, bool includeBaseFolder) { List invFolders = new List(items.Count); for (int i = 0; i < items.Count; i++) { OSDMap item = items[i] as OSDMap; if (item != null && item["Type"].AsString() == "Folder") { UUID folderID = item["ID"].AsUUID(); if (folderID == baseFolder && !includeBaseFolder) continue; invFolders.Add(new InventoryFolderBase( folderID, item["Name"].AsString(), item["OwnerID"].AsUUID(), (short)SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()), item["ParentID"].AsUUID(), (ushort)item["Version"].AsInteger() )); } } // m_log.Debug("[SIMIAN INVENTORY CONNECTOR]: Parsed " + invFolders.Count + " folders from SimianGrid response"); return invFolders; } private List GetItemsFromResponse(OSDArray items) { List invItems = new List(items.Count); for (int i = 0; i < items.Count; i++) { OSDMap item = items[i] as OSDMap; if (item != null && item["Type"].AsString() == "Item") { InventoryItemBase invItem = new InventoryItemBase(); invItem.AssetID = item["AssetID"].AsUUID(); invItem.AssetType = SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()); invItem.CreationDate = item["CreationDate"].AsInteger(); invItem.CreatorId = item["CreatorID"].AsString(); invItem.CreatorData = item["CreatorData"].AsString(); invItem.Description = item["Description"].AsString(); invItem.Folder = item["ParentID"].AsUUID(); invItem.ID = item["ID"].AsUUID(); invItem.InvType = SLUtil.ContentTypeToSLInvType(item["ContentType"].AsString()); invItem.Name = item["Name"].AsString(); invItem.Owner = item["OwnerID"].AsUUID(); OSDMap extraData = item["ExtraData"] as OSDMap; if (extraData != null && extraData.Count > 0) { invItem.Flags = extraData["Flags"].AsUInteger(); invItem.GroupID = extraData["GroupID"].AsUUID(); invItem.GroupOwned = extraData["GroupOwned"].AsBoolean(); invItem.SalePrice = extraData["SalePrice"].AsInteger(); invItem.SaleType = (byte)extraData["SaleType"].AsInteger(); OSDMap perms = extraData["Permissions"] as OSDMap; if (perms != null) { invItem.BasePermissions = perms["BaseMask"].AsUInteger(); invItem.CurrentPermissions = perms["OwnerMask"].AsUInteger(); invItem.EveryOnePermissions = perms["EveryoneMask"].AsUInteger(); invItem.GroupPermissions = perms["GroupMask"].AsUInteger(); invItem.NextPermissions = perms["NextOwnerMask"].AsUInteger(); } if (extraData.ContainsKey("LinkedItemType")) invItem.AssetType = SLUtil.ContentTypeToSLAssetType(extraData["LinkedItemType"].AsString()); } if (invItem.BasePermissions == 0) { m_log.InfoFormat("[SIMIAN INVENTORY CONNECTOR]: Forcing item permissions to full for item {0} ({1})", invItem.Name, invItem.ID); invItem.BasePermissions = (uint)PermissionMask.All; invItem.CurrentPermissions = (uint)PermissionMask.All; invItem.EveryOnePermissions = (uint)PermissionMask.All; invItem.GroupPermissions = (uint)PermissionMask.All; invItem.NextPermissions = (uint)PermissionMask.All; } invItems.Add(invItem); } } // m_log.Debug("[SIMIAN INVENTORY CONNECTOR]: Parsed " + invItems.Count + " items from SimianGrid response"); return invItems; } private bool MoveItems(UUID ownerID, List items, UUID destFolderID) { string[] itemIDs = new string[items.Count]; for (int i = 0; i < items.Count; i++) itemIDs[i] = items[i].ID.ToString(); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "MoveInventoryNodes" }, { "OwnerID", ownerID.ToString() }, { "FolderID", destFolderID.ToString() }, { "Items", String.Join(",", itemIDs) } }; OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to move " + items.Count + " items to " + destFolderID + ": " + response["Message"].AsString()); } return success; } private void UpdateGesture(UUID userID, UUID itemID, bool enabled) { OSDArray gestures = FetchGestures(userID); OSDArray newGestures = new OSDArray(); for (int i = 0; i < gestures.Count; i++) { UUID gesture = gestures[i].AsUUID(); if (gesture != itemID) newGestures.Add(OSD.FromUUID(gesture)); } if (enabled) newGestures.Add(OSD.FromUUID(itemID)); SaveGestures(userID, newGestures); } private OSDArray FetchGestures(UUID userID) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetUser" }, { "UserID", userID.ToString() } }; OSDMap response = SimianGrid.PostToService(m_userServerUrl, requestArgs); if (response["Success"].AsBoolean()) { OSDMap user = response["User"] as OSDMap; if (user != null && response.ContainsKey("Gestures")) { OSD gestures = OSDParser.DeserializeJson(response["Gestures"].AsString()); if (gestures != null && gestures is OSDArray) return (OSDArray)gestures; else m_log.Error("[SIMIAN INVENTORY CONNECTOR]: Unrecognized active gestures data for " + userID); } } else { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to fetch active gestures for " + userID + ": " + response["Message"].AsString()); } return new OSDArray(); } private void SaveGestures(UUID userID, OSDArray gestures) { NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddUserData" }, { "UserID", userID.ToString() }, { "Gestures", OSDParser.SerializeJsonString(gestures) } }; OSDMap response = SimianGrid.PostToService(m_userServerUrl, requestArgs); if (!response["Success"].AsBoolean()) { m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to save active gestures for " + userID + ": " + response["Message"].AsString()); } } } }