/* * 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.Net; using System.Xml; using System.Reflection; using System.Text; using System.Threading; using OpenSim.Framework; using OpenSim.Framework.Capabilities; using OpenSim.Framework.Client; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenMetaverse; using log4net; using Nini.Config; using Mono.Addins; using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "BasicInventoryAccessModule")] public class BasicInventoryAccessModule : INonSharedRegionModule, IInventoryAccessModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected bool m_Enabled = false; protected Scene m_Scene; protected IUserManagement m_UserManagement; protected IUserManagement UserManagementModule { get { if (m_UserManagement == null) m_UserManagement = m_Scene.RequestModuleInterface(); return m_UserManagement; } } public bool CoalesceMultipleObjectsToInventory { get; set; } #region INonSharedRegionModule public Type ReplaceableInterface { get { return null; } } public virtual string Name { get { return "BasicInventoryAccessModule"; } } public virtual void Initialise(IConfigSource source) { IConfig moduleConfig = source.Configs["Modules"]; if (moduleConfig != null) { string name = moduleConfig.GetString("InventoryAccessModule", ""); if (name == Name) { m_Enabled = true; InitialiseCommon(source); m_log.InfoFormat("[INVENTORY ACCESS MODULE]: {0} enabled.", Name); } } } /// /// Common module config for both this and descendant classes. /// /// protected virtual void InitialiseCommon(IConfigSource source) { IConfig inventoryConfig = source.Configs["Inventory"]; if (inventoryConfig != null) CoalesceMultipleObjectsToInventory = inventoryConfig.GetBoolean("CoalesceMultipleObjectsToInventory", true); else CoalesceMultipleObjectsToInventory = true; } public virtual void PostInitialise() { } public virtual void AddRegion(Scene scene) { if (!m_Enabled) return; m_Scene = scene; scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; } protected virtual void OnNewClient(IClientAPI client) { client.OnCreateNewInventoryItem += CreateNewInventoryItem; } public virtual void Close() { if (!m_Enabled) return; } public virtual void RemoveRegion(Scene scene) { if (!m_Enabled) return; m_Scene = null; } public virtual void RegionLoaded(Scene scene) { if (!m_Enabled) return; } #endregion #region Inventory Access /// /// Create a new inventory item. Called when the client creates a new item directly within their /// inventory (e.g. by selecting a context inventory menu option). /// /// /// /// /// /// /// /// /// /// /// public void CreateNewInventoryItem(IClientAPI remoteClient, UUID transactionID, UUID folderID, uint callbackID, string description, string name, sbyte invType, sbyte assetType, byte wearableType, uint nextOwnerMask, int creationDate) { m_log.DebugFormat("[INVENTORY ACCESS MODULE]: Received request to create inventory item {0} in folder {1}, transactionID {2}", name, folderID, transactionID); if (!m_Scene.Permissions.CanCreateUserInventory(invType, remoteClient.AgentId)) return; InventoryFolderBase folder = m_Scene.InventoryService.GetFolder(remoteClient.AgentId, folderID); if (folder == null && Enum.IsDefined(typeof(FolderType), (sbyte)invType)) { folder = m_Scene.InventoryService.GetFolderForType(remoteClient.AgentId, (FolderType)invType); if (folder != null) m_log.DebugFormat("[INVENTORY ACCESS MODULE]: Requested folder not found but found folder for type {0}", invType); } if (folder == null || folder.Owner != remoteClient.AgentId) return; if (transactionID != UUID.Zero) { IAgentAssetTransactions agentTransactions = m_Scene.AgentTransactionsModule; if (agentTransactions != null) { if (agentTransactions.HandleItemCreationFromTransaction( remoteClient, transactionID, folderID, callbackID, description, name, invType, assetType, wearableType, nextOwnerMask)) return; } } ScenePresence presence; if (m_Scene.TryGetScenePresence(remoteClient.AgentId, out presence)) { byte[] data = null; uint everyonemask = 0; uint groupmask = 0; if (invType == (sbyte)InventoryType.Landmark && presence != null) { string suffix = string.Empty, prefix = string.Empty; string strdata = GenerateLandmark(presence, out prefix, out suffix); data = Encoding.ASCII.GetBytes(strdata); name = prefix + name; description += suffix; groupmask = (uint)PermissionMask.AllAndExport; everyonemask = (uint)(PermissionMask.AllAndExport & ~PermissionMask.Modify); } AssetBase asset = m_Scene.CreateAsset(name, description, assetType, data, remoteClient.AgentId); m_Scene.AssetService.Store(asset); m_Scene.CreateNewInventoryItem( remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, name, description, 0, callbackID, asset.FullID, asset.Type, invType, (uint)PermissionMask.AllAndExport, // Base (uint)PermissionMask.AllAndExport, // Current everyonemask, nextOwnerMask, groupmask, creationDate, false); // Data from viewer } else { m_log.ErrorFormat( "[INVENTORY ACCESS MODULE]: ScenePresence for agent uuid {0} unexpectedly not found in CreateNewInventoryItem", remoteClient.AgentId); } } protected virtual string GenerateLandmark(ScenePresence presence, out string prefix, out string suffix) { prefix = string.Empty; suffix = string.Empty; Vector3 pos = presence.AbsolutePosition; return String.Format("Landmark version 2\nregion_id {0}\nlocal_pos {1} {2} {3}\nregion_handle {4}\n", presence.Scene.RegionInfo.RegionID, pos.X, pos.Y, pos.Z, presence.RegionHandle); } /// /// Capability originating call to update the asset of an item in an agent's inventory /// /// /// /// /// public virtual UUID CapsUpdateInventoryItemAsset(IClientAPI remoteClient, UUID itemID, byte[] data) { InventoryItemBase item = m_Scene.InventoryService.GetItem(remoteClient.AgentId, itemID); if (item == null) { m_log.ErrorFormat( "[INVENTORY ACCESS MODULE]: Could not find item {0} for caps inventory update", itemID); return UUID.Zero; } if (item.Owner != remoteClient.AgentId) return UUID.Zero; if ((InventoryType)item.InvType == InventoryType.Notecard) { if (!m_Scene.Permissions.CanEditNotecard(itemID, UUID.Zero, remoteClient.AgentId)) { remoteClient.SendAgentAlertMessage("Insufficient permissions to edit notecard", false); return UUID.Zero; } remoteClient.SendAlertMessage("Notecard saved"); } else if ((InventoryType)item.InvType == InventoryType.LSL) { if (!m_Scene.Permissions.CanEditScript(itemID, UUID.Zero, remoteClient.AgentId)) { remoteClient.SendAgentAlertMessage("Insufficient permissions to edit script", false); return UUID.Zero; } remoteClient.SendAlertMessage("Script saved"); } else if ((CustomInventoryType)item.InvType == CustomInventoryType.AnimationSet) { AnimationSet animSet = new AnimationSet(data); uint res = animSet.Validate(x => { const int required = (int)(PermissionMask.Transfer | PermissionMask.Copy); int perms = m_Scene.InventoryService.GetAssetPermissions(remoteClient.AgentId, x); // enforce previus perm rule if ((perms & required) != required) return 0; return (uint) perms; }); if(res == 0) { remoteClient.SendAgentAlertMessage("Not enought permissions on asset(s) referenced by animation set '{0}', update failed", false); return UUID.Zero; } } AssetBase asset = CreateAsset(item.Name, item.Description, (sbyte)item.AssetType, data, remoteClient.AgentId.ToString()); item.AssetID = asset.FullID; m_Scene.AssetService.Store(asset); m_Scene.InventoryService.UpdateItem(item); // remoteClient.SendInventoryItemCreateUpdate(item); return (asset.FullID); } public virtual bool UpdateInventoryItemAsset(UUID ownerID, InventoryItemBase item, AssetBase asset) { if (item != null && item.Owner == ownerID && asset != null) { // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Updating item {0} {1} with new asset {2}", // item.Name, item.ID, asset.ID); item.AssetID = asset.FullID; item.Description = asset.Description; item.Name = asset.Name; item.AssetType = asset.Type; item.InvType = (int)InventoryType.Object; m_Scene.AssetService.Store(asset); m_Scene.InventoryService.UpdateItem(item); return true; } else { m_log.ErrorFormat("[INVENTORY ACCESS MODULE]: Given invalid item for inventory update: {0}", (item == null || asset == null? "null item or asset" : "wrong owner")); return false; } } public virtual List CopyToInventory( DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient, bool asAttachment) { List copiedItems = new List(); Dictionary> bundlesToCopy = new Dictionary>(); if (CoalesceMultipleObjectsToInventory) { // The following code groups the SOG's by owner. No objects // belonging to different people can be coalesced, for obvious // reasons. foreach (SceneObjectGroup g in objectGroups) { if (!bundlesToCopy.ContainsKey(g.OwnerID)) bundlesToCopy[g.OwnerID] = new List(); bundlesToCopy[g.OwnerID].Add(g); } } else { // If we don't want to coalesce then put every object in its own bundle. foreach (SceneObjectGroup g in objectGroups) { List bundle = new List(); bundle.Add(g); bundlesToCopy[g.UUID] = bundle; } } // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Copying {0} object bundles to folder {1} action {2} for {3}", // bundlesToCopy.Count, folderID, action, remoteClient.Name); // Each iteration is really a separate asset being created, // with distinct destinations as well. foreach (List bundle in bundlesToCopy.Values) copiedItems.Add(CopyBundleToInventory(action, folderID, bundle, remoteClient, asAttachment)); return copiedItems; } /// /// Copy a bundle of objects to inventory. If there is only one object, then this will create an object /// item. If there are multiple objects then these will be saved as a single coalesced item. /// /// /// /// /// /// Should be true if the bundle is being copied as an attachment. This prevents /// attempted serialization of any script state which would abort any operating scripts. /// The inventory item created by the copy protected InventoryItemBase CopyBundleToInventory( DeRezAction action, UUID folderID, List objlist, IClientAPI remoteClient, bool asAttachment) { CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); Dictionary originalPositions = new Dictionary(); Dictionary originalRotations = new Dictionary(); // this possible is not needed if keyframes are saved // Dictionary originalKeyframes = new Dictionary(); foreach (SceneObjectGroup objectGroup in objlist) { if (objectGroup.RootPart.KeyframeMotion != null) { objectGroup.RootPart.KeyframeMotion.Suspend(); } objectGroup.RootPart.SetForce(Vector3.Zero); objectGroup.RootPart.SetAngularImpulse(Vector3.Zero, false); // originalKeyframes[objectGroup.UUID] = objectGroup.RootPart.KeyframeMotion; // objectGroup.RootPart.KeyframeMotion = null; Vector3 inventoryStoredPosition = objectGroup.AbsolutePosition; originalPositions[objectGroup.UUID] = inventoryStoredPosition; Quaternion inventoryStoredRotation = objectGroup.GroupRotation; originalRotations[objectGroup.UUID] = inventoryStoredRotation; // Restore attachment data after trip through the sim if (objectGroup.AttachmentPoint > 0) { inventoryStoredPosition = objectGroup.RootPart.AttachedPos; inventoryStoredRotation = objectGroup.RootPart.AttachRotation; if (objectGroup.RootPart.Shape.PCode != (byte) PCode.Tree && objectGroup.RootPart.Shape.PCode != (byte) PCode.NewTree) objectGroup.RootPart.Shape.LastAttachPoint = (byte)objectGroup.AttachmentPoint; } objectGroup.AbsolutePosition = inventoryStoredPosition; objectGroup.RootPart.RotationOffset = inventoryStoredRotation; // Make sure all bits but the ones we want are clear // on take. // This will be applied to the current perms, so // it will do what we want. objectGroup.RootPart.NextOwnerMask &= ((uint)PermissionMask.Copy | (uint)PermissionMask.Transfer | (uint)PermissionMask.Modify | (uint)PermissionMask.Export); objectGroup.RootPart.NextOwnerMask |= (uint)PermissionMask.Move; coa.Add(objectGroup); } string itemXml; // If we're being called from a script, then trying to serialize that same script's state will not complete // in any reasonable time period. Therefore, we'll avoid it. The worst that can happen is that if // the client/server crashes rather than logging out normally, the attachment's scripts will resume // without state on relog. Arguably, this is what we want anyway. if (objlist.Count > 1) itemXml = CoalescedSceneObjectsSerializer.ToXml(coa, !asAttachment); else itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0], !asAttachment); // Restore the position of each group now that it has been stored to inventory. foreach (SceneObjectGroup objectGroup in objlist) { objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; objectGroup.RootPart.RotationOffset = originalRotations[objectGroup.UUID]; // objectGroup.RootPart.KeyframeMotion = originalKeyframes[objectGroup.UUID]; if (objectGroup.RootPart.KeyframeMotion != null) objectGroup.RootPart.KeyframeMotion.Resume(); } InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Created item is {0}", // item != null ? item.ID.ToString() : "NULL"); if (item == null) return null; item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); item.CreatorData = objlist[0].RootPart.CreatorData; if (objlist.Count > 1) { item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; // If the objects have different creators then don't specify a creator at all foreach (SceneObjectGroup objectGroup in objlist) { if ((objectGroup.RootPart.CreatorID.ToString() != item.CreatorId) || (objectGroup.RootPart.CreatorData.ToString() != item.CreatorData)) { item.CreatorId = UUID.Zero.ToString(); item.CreatorData = string.Empty; break; } } } else { item.SaleType = objlist[0].RootPart.ObjectSaleType; item.SalePrice = objlist[0].RootPart.SalePrice; } AssetBase asset = CreateAsset( objlist[0].GetPartName(objlist[0].RootPart.LocalId), objlist[0].GetPartDescription(objlist[0].RootPart.LocalId), (sbyte)AssetType.Object, Utils.StringToBytes(itemXml), objlist[0].OwnerID.ToString()); m_Scene.AssetService.Store(asset); item.AssetID = asset.FullID; if (DeRezAction.SaveToExistingUserInventoryItem == action) { m_Scene.InventoryService.UpdateItem(item); } else { AddPermissions(item, objlist[0], objlist, remoteClient); item.CreationDate = Util.UnixTimeSinceEpoch(); item.Description = asset.Description; item.Name = asset.Name; item.AssetType = asset.Type; m_Scene.AddInventoryItem(item); if (remoteClient != null && item.Owner == remoteClient.AgentId) { remoteClient.SendInventoryItemCreateUpdate(item, 0); } else { ScenePresence notifyUser = m_Scene.GetScenePresence(item.Owner); if (notifyUser != null) { notifyUser.ControllingClient.SendInventoryItemCreateUpdate(item, 0); } } } // This is a hook to do some per-asset post-processing for subclasses that need that if (remoteClient != null && action != DeRezAction.Delete) ExportAsset(remoteClient.AgentId, asset.FullID); return item; } protected virtual void ExportAsset(UUID agentID, UUID assetID) { // nothing to do here } /// /// Add relevant permissions for an object to the item. /// /// /// /// /// /// protected InventoryItemBase AddPermissions( InventoryItemBase item, SceneObjectGroup so, List objsForEffectivePermissions, IClientAPI remoteClient) { uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move | PermissionMask.Export | PermissionMask.FoldedMask); foreach (SceneObjectGroup grp in objsForEffectivePermissions) { effectivePerms &= grp.CurrentAndFoldedNextPermissions(); } if (remoteClient != null && (remoteClient.AgentId != so.RootPart.OwnerID) && m_Scene.Permissions.PropagatePermissions()) { // apply parts inventory items next owner PermissionsUtil.ApplyNoModFoldedPermissions(effectivePerms, ref effectivePerms); // change to next owner uint basePerms = effectivePerms & so.RootPart.NextOwnerMask; // fix and update folded basePerms = PermissionsUtil.FixAndFoldPermissions(basePerms); item.BasePermissions = basePerms; item.CurrentPermissions = basePerms; item.NextPermissions = basePerms & so.RootPart.NextOwnerMask; item.EveryOnePermissions = basePerms & so.RootPart.EveryoneMask; item.GroupPermissions = basePerms & so.RootPart.GroupMask; // apply next owner perms on rez item.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm; } else { item.BasePermissions = effectivePerms; item.CurrentPermissions = effectivePerms; item.NextPermissions = so.RootPart.NextOwnerMask & effectivePerms; item.EveryOnePermissions = so.RootPart.EveryoneMask & effectivePerms; item.GroupPermissions = so.RootPart.GroupMask & effectivePerms; item.CurrentPermissions &= ((uint)PermissionMask.Copy | (uint)PermissionMask.Transfer | (uint)PermissionMask.Modify | (uint)PermissionMask.Move | (uint)PermissionMask.Export | (uint)PermissionMask.FoldedMask); // Preserve folded permissions ?? } return item; } /// /// Create an item using details for the given scene object. /// /// /// /// /// /// protected InventoryItemBase CreateItemForObject( DeRezAction action, IClientAPI remoteClient, SceneObjectGroup so, UUID folderID) { // m_log.DebugFormat( // "[BASIC INVENTORY ACCESS MODULE]: Creating item for object {0} {1} for folder {2}, action {3}", // so.Name, so.UUID, folderID, action); // // Get the user info of the item destination // UUID userID = UUID.Zero; if (action == DeRezAction.Take || action == DeRezAction.TakeCopy || action == DeRezAction.SaveToExistingUserInventoryItem) { // Take or take copy require a taker // Saving changes requires a local user // if (remoteClient == null) return null; userID = remoteClient.AgentId; // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Target of {0} in CreateItemForObject() is {1} {2}", // action, remoteClient.Name, userID); } else if (so.RootPart.OwnerID == so.RootPart.GroupID) { // Group owned objects go to the last owner before the object was transferred. userID = so.RootPart.LastOwnerID; } else { // Other returns / deletes go to the object owner // userID = so.RootPart.OwnerID; // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Target of {0} in CreateItemForObject() is object owner {1}", // action, userID); } if (userID == UUID.Zero) // Can't proceed { return null; } // If we're returning someone's item, it goes back to the // owner's Lost And Found folder. // Delete is treated like return in this case // Deleting your own items makes them go to trash // InventoryFolderBase folder = null; InventoryItemBase item = null; if (DeRezAction.SaveToExistingUserInventoryItem == action) { item = m_Scene.InventoryService.GetItem(userID, so.RootPart.FromUserInventoryItemID); //item = userInfo.RootFolder.FindItem( // objectGroup.RootPart.FromUserInventoryItemID); if (null == item) { m_log.DebugFormat( "[INVENTORY ACCESS MODULE]: Object {0} {1} scheduled for save to inventory has already been deleted.", so.Name, so.UUID); return null; } } else { // Folder magic // if (action == DeRezAction.Delete) { // Deleting someone else's item // if (remoteClient == null || so.OwnerID != remoteClient.AgentId) { folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); } else { folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Trash); } } else if (action == DeRezAction.Return) { // Dump to lost + found unconditionally // folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); } if (folderID == UUID.Zero && folder == null) { if (action == DeRezAction.Delete) { // Deletes go to trash by default // folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Trash); } else { if (remoteClient == null || so.RootPart.OwnerID != remoteClient.AgentId) { // Taking copy of another person's item. Take to // Objects folder. folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Object); so.FromFolderID = UUID.Zero; } else { // Catch all. Use lost & found // folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); } } } // Override and put into where it came from, if it came // from anywhere in inventory and the owner is taking it back. // if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) { if (so.FromFolderID != UUID.Zero && so.RootPart.OwnerID == remoteClient.AgentId) { folder = m_Scene.InventoryService.GetFolder(userID, so.FromFolderID); if(folder.Type == 14 || folder.Type == 16) { // folder.Type = 6; folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Object); } } } if (folder == null) // None of the above { folder = new InventoryFolderBase(folderID); if (folder == null) // Nowhere to put it { return null; } } item = new InventoryItemBase(); item.ID = UUID.Random(); item.InvType = (int)InventoryType.Object; item.Folder = folder.ID; item.Owner = userID; } return item; } // compatibility do not use public virtual SceneObjectGroup RezObject( IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) { return RezObject(remoteClient, itemID, UUID.Zero, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, RezSelected, RemoveItem, fromTaskID, attachment); } public virtual SceneObjectGroup RezObject( IClientAPI remoteClient, UUID itemID, UUID rezGroupID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) { // m_log.DebugFormat("[INVENTORY ACCESS MODULE]: RezObject for {0}, item {1}", remoteClient.Name, itemID); InventoryItemBase item = m_Scene.InventoryService.GetItem(remoteClient.AgentId, itemID); if (item == null) { return null; } item.Owner = remoteClient.AgentId; return RezObject( remoteClient, item, rezGroupID, item.AssetID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, RezSelected, RemoveItem, fromTaskID, attachment); } // compatility public virtual SceneObjectGroup RezObject( IClientAPI remoteClient, InventoryItemBase item, UUID assetID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) { return RezObject(remoteClient, item, UUID.Zero, assetID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, RezSelected, RemoveItem, fromTaskID, attachment); } public virtual SceneObjectGroup RezObject( IClientAPI remoteClient, InventoryItemBase item, UUID groupID, UUID assetID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) { AssetBase rezAsset = m_Scene.AssetService.Get(assetID.ToString()); if (rezAsset == null) { if (item != null) { m_log.WarnFormat( "[InventoryAccessModule]: Could not find asset {0} for item {1} {2} for {3} in RezObject()", assetID, item.Name, item.ID, remoteClient.Name); remoteClient.SendAgentAlertMessage(string.Format("Unable to rez: could not find asset {0} for item {1}.", assetID, item.Name), false); } else { m_log.WarnFormat( "[INVENTORY ACCESS MODULE]: Could not find asset {0} for {1} in RezObject()", assetID, remoteClient.Name); remoteClient.SendAgentAlertMessage(string.Format("Unable to rez: could not find asset {0}.", assetID), false); } return null; } if(rezAsset.Data == null || rezAsset.Data.Length == 0) { m_log.WarnFormat( "[INVENTORY ACCESS MODULE]: missing data in asset {0} to RezObject()", assetID, remoteClient.Name); remoteClient.SendAgentAlertMessage(string.Format("Unable to rez: missing data in asset {0} ", assetID), false); return null; } SceneObjectGroup group = null; List objlist; List veclist; Vector3 bbox; float offsetHeight; byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); Vector3 pos; bool single = m_Scene.GetObjectsToRez( rezAsset.Data, attachment, out objlist, out veclist, out bbox, out offsetHeight); if (single) { pos = m_Scene.GetNewRezLocation( RayStart, RayEnd, RayTargetID, Quaternion.Identity, BypassRayCast, bRayEndIsIntersection, true, bbox, false); pos.Z += offsetHeight; } else { pos = m_Scene.GetNewRezLocation(RayStart, RayEnd, RayTargetID, Quaternion.Identity, BypassRayCast, bRayEndIsIntersection, true, bbox, false); pos -= bbox / 2; } int primcount = 0; foreach (SceneObjectGroup g in objlist) primcount += g.PrimCount; if (!m_Scene.Permissions.CanRezObject( primcount, remoteClient.AgentId, pos) && !attachment) { // The client operates in no fail mode. It will // have already removed the item from the folder // if it's no copy. // Put it back if it's not an attachment // if (item != null) { if (((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) && (!attachment)) remoteClient.SendBulkUpdateInventory(item); } return null; } if (item != null && !DoPreRezWhenFromItem(remoteClient, item, objlist, pos, veclist, attachment)) return null; for (int i = 0; i < objlist.Count; i++) { group = objlist[i]; SceneObjectPart rootPart = group.RootPart; // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Preparing to rez {0} {1} {2} ownermask={3:X} nextownermask={4:X} groupmask={5:X} everyonemask={6:X} for {7}", // group.Name, group.LocalId, group.UUID, // group.RootPart.OwnerMask, group.RootPart.NextOwnerMask, group.RootPart.GroupMask, group.RootPart.EveryoneMask, // remoteClient.Name); // Vector3 storedPosition = group.AbsolutePosition; if (group.UUID == UUID.Zero) { m_log.Debug("[INVENTORY ACCESS MODULE]: Object has UUID.Zero! Position 3"); } // if this was previously an attachment and is now being rezzed, // save the old attachment info. if (group.IsAttachment == false && group.RootPart.Shape.State != 0) { group.RootPart.AttachedPos = group.AbsolutePosition; group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; } if (item == null) { // Change ownership. Normally this is done in DoPreRezWhenFromItem(), but in this case we must do it here. foreach (SceneObjectPart part in group.Parts) { // Make the rezzer the owner, as this is not necessarily set correctly in the serialized asset. part.LastOwnerID = part.OwnerID; part.OwnerID = remoteClient.AgentId; part.RezzerID = remoteClient.AgentId; } } group.ResetIDs(); if (!attachment) { // If it's rezzed in world, select it. Much easier to // find small items. // foreach (SceneObjectPart part in group.Parts) { part.CreateSelected = true; } if (rootPart.Shape.PCode == (byte)PCode.Prim) group.ClearPartAttachmentData(); } else { group.IsAttachment = true; } group.SetGroup(groupID, remoteClient); // If we're rezzing an attachment then don't ask // AddNewSceneObject() to update the client since // we'll be doing that later on. Scheduling more than // one full update during the attachment // process causes some clients to fail to display the // attachment properly. if (!attachment) { group.AbsolutePosition = pos + veclist[i]; m_Scene.AddNewSceneObject(group, true, false); // Fire on_rez group.CreateScriptInstances(0, true, m_Scene.DefaultScriptEngine, 1); rootPart.ParentGroup.ResumeScripts(); group.ScheduleGroupForFullUpdate(); } else m_Scene.AddNewSceneObject(group, true, false); // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: Rezzed {0} {1} {2} ownermask={3:X} nextownermask={4:X} groupmask={5:X} everyonemask={6:X} for {7}", // group.Name, group.LocalId, group.UUID, // group.RootPart.OwnerMask, group.RootPart.NextOwnerMask, group.RootPart.GroupMask, group.RootPart.EveryoneMask, // remoteClient.Name); } // group.SetGroup(remoteClient.ActiveGroupId, remoteClient); if (item != null) DoPostRezWhenFromItem(item, attachment); return group; } /// /// Do pre-rez processing when the object comes from an item. /// /// /// /// /// /// /// List of vector position adjustments for a coalesced objects. For ordinary objects /// this list will contain just Vector3.Zero. The order of adjustments must match the order of objlist /// /// /// true if we can processed with rezzing, false if we need to abort private bool DoPreRezWhenFromItem( IClientAPI remoteClient, InventoryItemBase item, List objlist, Vector3 pos, List veclist, bool isAttachment) { UUID fromUserInventoryItemId = UUID.Zero; // If we have permission to copy then link the rezzed object back to the user inventory // item that it came from. This allows us to enable 'save object to inventory' if (!m_Scene.Permissions.BypassPermissions()) { if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == (uint)PermissionMask.Copy && (item.Flags & (uint)InventoryItemFlags.ObjectHasMultipleItems) == 0) { fromUserInventoryItemId = item.ID; } } else { if ((item.Flags & (uint)InventoryItemFlags.ObjectHasMultipleItems) == 0) { // Brave new fullperm world fromUserInventoryItemId = item.ID; } } for (int i = 0; i < objlist.Count; i++) { SceneObjectGroup g = objlist[i]; if (!m_Scene.Permissions.CanRezObject( g.PrimCount, remoteClient.AgentId, pos + veclist[i]) && !isAttachment) { // The client operates in no fail mode. It will // have already removed the item from the folder // if it's no copy. // Put it back if it's not an attachment // if (((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) && (!isAttachment)) remoteClient.SendBulkUpdateInventory(item); ILandObject land = m_Scene.LandChannel.GetLandObject(pos.X, pos.Y); remoteClient.SendAlertMessage(string.Format( "Can't rez object '{0}' at <{1:F3}, {2:F3}, {3:F3}> on parcel '{4}' in region {5}.", item.Name, pos.X, pos.Y, pos.Z, land != null ? land.LandData.Name : "Unknown", m_Scene.Name)); return false; } } for (int i = 0; i < objlist.Count; i++) { SceneObjectGroup so = objlist[i]; SceneObjectPart rootPart = so.RootPart; // Since renaming the item in the inventory does not // affect the name stored in the serialization, transfer // the correct name from the inventory to the // object itself before we rez. // // Only do these for the first object if we are rezzing a coalescence. // nahh dont mess with coalescence objects, // the name in inventory can be change for inventory purpuses only if (objlist.Count == 1) { rootPart.Name = item.Name; rootPart.Description = item.Description; } if ((item.Flags & (uint)InventoryItemFlags.ObjectSlamSale) != 0) { rootPart.ObjectSaleType = item.SaleType; rootPart.SalePrice = item.SalePrice; } so.FromFolderID = item.Folder; // m_log.DebugFormat( // "[INVENTORY ACCESS MODULE]: rootPart.OwnedID {0}, item.Owner {1}, item.CurrentPermissions {2:X}", // rootPart.OwnerID, item.Owner, item.CurrentPermissions); if ((rootPart.OwnerID != item.Owner) || (item.CurrentPermissions & (uint)PermissionMask.Slam) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) { //Need to kill the for sale here rootPart.ObjectSaleType = 0; rootPart.SalePrice = 10; if (m_Scene.Permissions.PropagatePermissions()) { foreach (SceneObjectPart part in so.Parts) { part.GroupMask = 0; // DO NOT propagate here if( part.OwnerID != part.GroupID) part.LastOwnerID = part.OwnerID; part.OwnerID = item.Owner; part.RezzerID = item.Owner; part.Inventory.ChangeInventoryOwner(item.Owner); // Reconstruct the original item's base permissions. They // can be found in the lower (folded) bits. if ((item.BasePermissions & (uint)PermissionMask.FoldedMask) != 0) { // We have permissions stored there so use them part.NextOwnerMask = ((item.BasePermissions & (uint)PermissionMask.FoldedMask) << (int)PermissionMask.FoldingShift); part.NextOwnerMask |= (uint)PermissionMask.Move; } else { // This is a legacy object and we can't avoid the issues that // caused perms loss or escalation before, treat it the legacy // way. part.NextOwnerMask = item.NextPermissions; } } so.ApplyNextOwnerPermissions(); // In case the user has changed flags on a received item // we have to apply those changes after the slam. Else we // get a net loss of permissions. // On legacy objects, this opts for a loss of permissions rather // than the previous handling that allowed escalation. foreach (SceneObjectPart part in so.Parts) { if ((item.Flags & (uint)InventoryItemFlags.ObjectHasMultipleItems) == 0) { part.GroupMask = item.GroupPermissions & part.BaseMask; part.EveryoneMask = item.EveryOnePermissions & part.BaseMask; part.NextOwnerMask = item.NextPermissions & part.BaseMask; } } } } else { foreach (SceneObjectPart part in so.Parts) { part.FromUserInventoryItemID = fromUserInventoryItemId; if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0) part.EveryoneMask = item.EveryOnePermissions; if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0) part.NextOwnerMask = item.NextPermissions; if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0) part.GroupMask = item.GroupPermissions; } } rootPart.TrimPermissions(); so.InvalidateDeepEffectivePerms(); if (isAttachment) so.FromItemID = item.ID; } return true; } /// /// Do post-rez processing when the object comes from an item. /// /// /// private void DoPostRezWhenFromItem(InventoryItemBase item, bool isAttachment) { if (!m_Scene.Permissions.BypassPermissions()) { if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) { // If this is done on attachments, no // copy ones will be lost, so avoid it // if (!isAttachment) { List uuids = new List(); uuids.Add(item.ID); m_Scene.InventoryService.DeleteItems(item.Owner, uuids); } } } } protected void AddUserData(SceneObjectGroup sog) { UserManagementModule.AddUser(sog.RootPart.CreatorID, sog.RootPart.CreatorData); foreach (SceneObjectPart sop in sog.Parts) UserManagementModule.AddUser(sop.CreatorID, sop.CreatorData); } public virtual void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver) { } public virtual bool CanGetAgentInventoryItem(IClientAPI remoteClient, UUID itemID, UUID requestID) { InventoryItemBase assetRequestItem = GetItem(remoteClient.AgentId, itemID); if (assetRequestItem == null) { ILibraryService lib = m_Scene.RequestModuleInterface(); if (lib != null) assetRequestItem = lib.LibraryRootFolder.FindItem(itemID); if (assetRequestItem == null) return false; } // At this point, we need to apply perms // only to notecards and scripts. All // other asset types are always available // if (assetRequestItem.AssetType == (int)AssetType.LSLText) { if (!m_Scene.Permissions.CanViewScript(itemID, UUID.Zero, remoteClient.AgentId)) { remoteClient.SendAgentAlertMessage("Insufficient permissions to view script", false); return false; } } else if (assetRequestItem.AssetType == (int)AssetType.Notecard) { if (!m_Scene.Permissions.CanViewNotecard(itemID, UUID.Zero, remoteClient.AgentId)) { remoteClient.SendAgentAlertMessage("Insufficient permissions to view notecard", false); return false; } } if (assetRequestItem.AssetID != requestID) { m_log.WarnFormat( "[INVENTORY ACCESS MODULE]: {0} requested asset {1} from item {2} but this does not match item's asset {3}", Name, requestID, itemID, assetRequestItem.AssetID); return false; } return true; } public virtual bool IsForeignUser(UUID userID, out string assetServerURL) { assetServerURL = string.Empty; return false; } #endregion #region Misc /// /// Create a new asset data structure. /// /// /// /// /// /// /// private AssetBase CreateAsset(string name, string description, sbyte assetType, byte[] data, string creatorID) { AssetBase asset = new AssetBase(UUID.Random(), name, assetType, creatorID); asset.Description = description; asset.Data = (data == null) ? new byte[1] : data; return asset; } protected virtual InventoryItemBase GetItem(UUID agentID, UUID itemID) { IInventoryService invService = m_Scene.RequestModuleInterface(); InventoryItemBase item = invService.GetItem(agentID, itemID); if (item != null && item.CreatorData != null && item.CreatorData != string.Empty) UserManagementModule.AddUser(item.CreatorIdAsUuid, item.CreatorData); return item; } #endregion } }