/* * 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.Reflection; using log4net; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications.Cache; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer { public class InventoryTransferModule : IInventoryTransferModule, IRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// private List m_Scenelist = new List(); private Dictionary m_AgentRegions = new Dictionary(); private IMessageTransferModule m_TransferModule = null; #region IRegionModule Members public void Initialise(Scene scene, IConfigSource config) { if (config.Configs["Messaging"] != null) { // Allow disabling this module in config // if (config.Configs["Messaging"].GetString( "InventoryTransferModule", "InventoryTransferModule") != "InventoryTransferModule") return; } if (!m_Scenelist.Contains(scene)) { if (m_Scenelist.Count == 0) { m_TransferModule = scene.RequestModuleInterface(); if (m_TransferModule == null) m_log.Error("[INVENTORY TRANSFER] No Message transfer module found, transfers will be local only"); } m_Scenelist.Add(scene); scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnClientClosed += ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; } } public void PostInitialise() { } public void Close() { } public string Name { get { return "InventoryModule"; } } public bool IsSharedModule { get { return true; } } #endregion private void OnNewClient(IClientAPI client) { // Inventory giving is conducted via instant message client.OnInstantMessage += OnInstantMessage; } private Scene FindClientScene(UUID agentId) { lock (m_Scenelist) { foreach (Scene scene in m_Scenelist) { ScenePresence presence = scene.GetScenePresence(agentId); if (presence != null) { if (!presence.IsChildAgent) return scene; } } } return null; } private void OnInstantMessage(IClientAPI client, GridInstantMessage im) { Scene scene = FindClientScene(client.AgentId); if (scene == null) // Something seriously wrong here. return; if (im.dialog == (byte) InstantMessageDialog.InventoryOffered) { //m_log.DebugFormat("Asset type {0}", ((AssetType)im.binaryBucket[0])); ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID)); UUID copyID; // First byte is the asset type AssetType assetType = (AssetType)im.binaryBucket[0]; if (AssetType.Folder == assetType) { UUID folderID = new UUID(im.binaryBucket, 1); m_log.DebugFormat("[AGENT INVENTORY]: Inserting original folder {0} "+ "into agent {1}'s inventory", folderID, new UUID(im.toAgentID)); InventoryFolderImpl folderCopy = scene.GiveInventoryFolder(new UUID(im.toAgentID), client.AgentId, folderID, UUID.Zero); if (folderCopy == null) { client.SendAgentAlertMessage("Can't find folder to give. Nothing given.", false); return; } // The outgoing binary bucket should contain only the byte which signals an asset folder is // being copied and the following bytes for the copied folder's UUID copyID = folderCopy.ID; byte[] copyIDBytes = copyID.GetBytes(); im.binaryBucket = new byte[1 + copyIDBytes.Length]; im.binaryBucket[0] = (byte)AssetType.Folder; Array.Copy(copyIDBytes, 0, im.binaryBucket, 1, copyIDBytes.Length); if (user != null && !user.IsChildAgent) { user.ControllingClient.SendBulkUpdateInventory(folderCopy); } } else { // First byte of the array is probably the item type // Next 16 bytes are the UUID UUID itemID = new UUID(im.binaryBucket, 1); m_log.DebugFormat("[AGENT INVENTORY]: Inserting item {0} "+ "into agent {1}'s inventory", itemID, new UUID(im.toAgentID)); InventoryItemBase itemCopy = scene.GiveInventoryItem( new UUID(im.toAgentID), client.AgentId, itemID); if (itemCopy == null) { client.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); return; } copyID = itemCopy.ID; Array.Copy(copyID.GetBytes(), 0, im.binaryBucket, 1, 16); if (user != null && !user.IsChildAgent) { user.ControllingClient.SendBulkUpdateInventory(itemCopy); } } // Send the IM to the recipient. The item is already // in their inventory, so it will not be lost if // they are offline. // if (user != null && !user.IsChildAgent) { // And notify. Transaction ID is the item ID. We get that // same ID back on the reply so we know what to act on // user.ControllingClient.SendInstantMessage(im); return; } else { if (m_TransferModule != null) m_TransferModule.SendInstantMessage(im, delegate(bool success) {} ); } } else if (im.dialog == (byte) InstantMessageDialog.InventoryAccepted) { ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID)); if (user != null) // Local { user.ControllingClient.SendInstantMessage(im); } else { if (m_TransferModule != null) m_TransferModule.SendInstantMessage(im, delegate(bool success) {} ); } } else if (im.dialog == (byte) InstantMessageDialog.InventoryDeclined) { // Here, the recipient is local and we can assume that the // inventory is loaded. Courtesy of the above bulk update, // It will have been pushed to the client, too // CachedUserInfo userInfo = scene.CommsManager.UserProfileCacheService. GetUserDetails(client.AgentId); if (userInfo != null) { InventoryFolderImpl trashFolder = userInfo.FindFolderForType((int)AssetType.TrashFolder); UUID inventoryEntityID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip InventoryItemBase item = userInfo.RootFolder.FindItem(inventoryEntityID); InventoryFolderBase folder = null; if (item != null && trashFolder != null) { item.Folder = trashFolder.ID; userInfo.DeleteItem(inventoryEntityID); scene.AddInventoryItem(client, item); } else { folder = userInfo.RootFolder.FindFolder(inventoryEntityID); if (folder != null & trashFolder != null) { userInfo.MoveFolder(inventoryEntityID, trashFolder.ID); } } if ((null == item && null == folder) | null == trashFolder) { string reason = String.Empty; if (trashFolder == null) reason += " Trash folder not found."; if (item == null) reason += " Item not found."; if (folder == null) reason += " Folder not found."; client.SendAgentAlertMessage("Unable to delete "+ "received inventory" + reason, false); } } ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID)); if (user != null) // Local { user.ControllingClient.SendInstantMessage(im); } else { if (m_TransferModule != null) m_TransferModule.SendInstantMessage(im, delegate(bool success) {} ); } } } public void SetRootAgentScene(UUID agentID, Scene scene) { m_AgentRegions[agentID] = scene; } public bool NeedSceneCacheClear(UUID agentID, Scene scene) { if (!m_AgentRegions.ContainsKey(agentID)) { // Since we can get here two ways, we need to scan // the scenes here. This is somewhat more expensive // but helps avoid a nasty bug // foreach (Scene s in m_Scenelist) { ScenePresence presence; if (s.TryGetAvatar(agentID, out presence)) { // If the agent is in this scene, then we // are being called twice in a single // teleport. This is wasteful of cycles // but harmless due to this 2nd level check // // If the agent is found in another scene // then the list wasn't current // // If the agent is totally unknown, then what // are we even doing here?? // if (s == scene) { //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); return true; } else { //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); return false; } } } //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); return true; } // The agent is left in current Scene, so we must be // going to another instance // if (m_AgentRegions[agentID] == scene) { //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); m_AgentRegions.Remove(agentID); return true; } // Another region has claimed the agent // //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); return false; } public void ClientLoggedOut(UUID agentID) { if (m_AgentRegions.ContainsKey(agentID)) m_AgentRegions.Remove(agentID); } /// /// /// /// private void OnGridInstantMessage(GridInstantMessage msg) { // Check if this is ours to handle // if (msg.dialog != (byte) InstantMessageDialog.InventoryOffered) return; if (msg.binaryBucket.Length < 17) // Invalid return; Scene scene = FindClientScene(new UUID(msg.toAgentID)); // Find agent to deliver to // ScenePresence user = scene.GetScenePresence(new UUID(msg.toAgentID)); if (user == null) // Shouldn't happen { m_log.Debug("[INVENTORY TRANSFER] Can't find recipient"); return; } CachedUserInfo userInfo = scene.CommsManager.UserProfileCacheService. GetUserDetails(user.ControllingClient.AgentId); if (userInfo == null) { m_log.Debug("[INVENTORY TRANSFER] Can't find user info of recipient"); return; } AssetType assetType = (AssetType)msg.binaryBucket[0]; if (AssetType.Folder == assetType) { UUID folderID = new UUID(msg.binaryBucket, 1); InventoryFolderBase folder = new InventoryFolderBase(); folder.ID = folderID; folder.Owner = user.ControllingClient.AgentId; // Fetch from database // if (!userInfo.QueryFolder(folder)) { m_log.Debug("[INVENTORY TRANSFER] Can't find folder to give"); return; } // Get folder info // InventoryFolderImpl folderInfo = userInfo.RootFolder.FindFolder(folder.ID); if (folderInfo == null) { m_log.Debug("[INVENTORY TRANSFER] Can't retrieve folder to give"); return; } user.ControllingClient.SendBulkUpdateInventory(folderInfo); // This unelegant, slow kludge is to reload the folders and // items. Since a folder give can transfer subfolders and // items, this is the easiest way to pull that stuff in // userInfo.DropInventory(); userInfo.FetchInventory(); // Deliver message // user.ControllingClient.SendInstantMessage(msg); } else { UUID itemID = new UUID(msg.binaryBucket, 1); InventoryItemBase item = new InventoryItemBase(); item.ID = itemID; item.Owner = user.ControllingClient.AgentId; // Fetch from database // if (!userInfo.QueryItem(item)) { m_log.Debug("[INVENTORY TRANSFER] Can't find item to give"); return; } // Get item info // item = userInfo.RootFolder.FindItem(item.ID); if (item == null) { m_log.Debug("[INVENTORY TRANSFER] Can't retrieve item to give"); return; } // Update item to viewer (makes it appear in proper folder) // user.ControllingClient.SendBulkUpdateInventory(item); // Deliver message // user.ControllingClient.SendInstantMessage(msg); } } } }