diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index a25e1d09c3..7c993ae17f 100755 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -40,6 +40,7 @@ using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; using System.Text; using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; +using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { @@ -103,18 +104,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); if (!m_groupsEnabled) - { return; - } if (groupsConfig.GetString("Module", "Default") != Name) { m_groupsEnabled = false; - return; } - m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name); + m_log.InfoFormat("[xmlGROUPS]: Initializing {0}", this.Name); m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true); m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false); @@ -404,9 +402,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) { if (m_debugEnabled) - m_log.DebugFormat( - "[GROUPS]: {0} called for {1}, message type {2}", - System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name, (InstantMessageDialog)im.dialog); + m_log.DebugFormat("[xmlGROUPS]: OnInstantMessage called for {0}, message type {1}", + remoteClient.Name, (InstantMessageDialog)im.dialog); + + if (!m_groupNoticesEnabled || remoteClient == null || !remoteClient.IsActive || remoteClient.AgentId == UUID.Zero) + return; + + Scene scene = (Scene)remoteClient.Scene; + if(scene == null) + return; + + UUID remoteAgentID = remoteClient.AgentId; // Group invitations if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) @@ -416,11 +422,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (inviteInfo == null) { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Received an Invite IM for an invite that does not exist {0}.", inviteID); + if (m_debugEnabled) + m_log.WarnFormat("[xmlGROUPS]: Received an Invite IM for an invite that does not exist {0}.", inviteID); return; } - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); UUID fromAgentID = new UUID(im.fromAgentID); if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) @@ -428,10 +436,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Accept if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice."); + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: Received an accept invite notice."); // and the sessionid is the role - m_groupData.AddAgentToGroup(GetRequestingAgentID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); + m_groupData.AddAgentToGroup(remoteAgentID, inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; @@ -461,175 +470,275 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Reject if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice."); - m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID); + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(remoteAgentID, inviteID); } } + return; } // Group notices if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) { - if (!m_groupNoticesEnabled) - { - return; - } - UUID GroupID = new UUID(im.toAgentID); - if (m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null) != null) + GroupMembershipData grpMemberData = m_groupData.GetAgentGroupMembership(remoteAgentID, remoteAgentID, GroupID); + if (grpMemberData == null) { - UUID NoticeID = UUID.Random(); - string Subject = im.message.Substring(0, im.message.IndexOf('|')); - string Message = im.message.Substring(Subject.Length + 1); - - InventoryItemBase item = null; - bool hasAttachment = false; - UUID itemID = UUID.Zero; //Assignment to quiet compiler - UUID ownerID = UUID.Zero; //Assignment to quiet compiler - byte[] bucket; - - if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) - { - string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); - binBucket = binBucket.Remove(0, 14).Trim(); - - OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); - if (binBucketOSD is OSD) - { - OSDMap binBucketMap = (OSDMap)binBucketOSD; - - itemID = binBucketMap["item_id"].AsUUID(); - ownerID = binBucketMap["owner_id"].AsUUID(); - - //Attempt to get the details of the attached item. - //If sender doesn't own the attachment, the item - //variable will be set to null and attachment will - //not be included with the group notice. - Scene scene = (Scene)remoteClient.Scene; - item = scene.InventoryService.GetItem(ownerID, itemID); - - if (item != null) - { - //Got item details so include the attachment. - hasAttachment = true; - } - } - else - { - m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); - } - } - - if (hasAttachment) - { - //Bucket contains information about attachment. - // - //Byte offset and description of bucket data: - //0: 1 byte indicating if attachment is present - //1: 1 byte indicating the type of attachment - //2: 16 bytes - Group UUID - //18: 16 bytes - UUID of the attachment owner - //34: 16 bytes - UUID of the attachment - //50: variable - Name of the attachment - //??: NUL byte to terminate the attachment name - byte[] name = Encoding.UTF8.GetBytes(item.Name); - bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name - bucket[0] = 1; //Has attachment flag - bucket[1] = (byte)item.InvType; //Type of Attachment - GroupID.ToBytes(bucket, 2); - ownerID.ToBytes(bucket, 18); - itemID.ToBytes(bucket, 34); - name.CopyTo(bucket, 50); - } - else - { - bucket = new byte[19]; - bucket[0] = 0; //Has attachment flag - bucket[1] = 0; //Type of attachment - GroupID.ToBytes(bucket, 2); - bucket[18] = 0; //NUL terminate name of attachment - } - - m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); - if (OnNewGroupNotice != null) - { - OnNewGroupNotice(GroupID, NoticeID); - } - - if (m_debugEnabled) - { - foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID)) - { - if (m_debugEnabled) - { - UserAccount targetUser - = m_sceneList[0].UserAccountService.GetUserAccount( - remoteClient.Scene.RegionInfo.ScopeID, member.AgentID); - - if (targetUser != null) - { - m_log.DebugFormat( - "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", - NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices); - } - else - { - m_log.DebugFormat( - "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", - NoticeID, member.AgentID, member.AcceptNotices); - } - } - } - } - - GridInstantMessage msg - = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); - - if (m_groupsMessagingModule != null) - m_groupsMessagingModule.SendMessageToGroup( - msg, GroupID, remoteClient.AgentId, gmd => gmd.AcceptNotices); + remoteClient.SendAgentAlertMessage("Group membership not found", false); + return; } - } - if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) - { - //Is bucket large enough to hold UUID of the attachment? - if (im.binaryBucket.Length < 16) + if ((grpMemberData.GroupPowers & (ulong)GroupPowers.SendNotices) == 0) + { + remoteClient.SendAgentAlertMessage("No permission to send notice to group", false); + return; + } + + int index = im.message.IndexOf('|'); + if(index < 0) return; - UUID noticeID = new UUID(im.imSessionID); + string Subject = im.message.Substring(0, index); + string Message = im.message.Substring(index + 1); - if (m_debugEnabled) - m_log.DebugFormat("[GROUPS]: Requesting notice {0} for {1}", noticeID, remoteClient.AgentId); + InventoryItemBase item = null; + bool hasAttachment = false; + UUID itemID = UUID.Zero; + UUID ownerID = UUID.Zero; + byte[] bucket; - GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID); - if (notice != null) + if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) { - UUID giver = new UUID(notice.BinaryBucket, 18); - UUID attachmentUUID = new UUID(notice.BinaryBucket, 34); - - if (m_debugEnabled) - m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); - - string message; - InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, - giver, attachmentUUID, out message); - - if (itemCopy == null) + try { - remoteClient.SendAgentAlertMessage(message, false); + string binBucket = Utils.BytesToString(im.binaryBucket); + binBucket = binBucket.Substring(15); // remove extra LLSD pre header + + OSDMap binBucketMAP = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); + itemID = binBucketMAP["item_id"].AsUUID(); + ownerID = binBucketMAP["owner_id"].AsUUID(); + } + catch + { + m_log.DebugFormat("[xmlGROUPS]: failed to decode group notice bucket"); return; } - remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + if(itemID != UUID.Zero && ownerID != UUID.Zero) + { + item = scene.InventoryService.GetItem(ownerID, itemID); + if (item != null) + { + if((item.CurrentPermissions & (uint)(PermissionMask.Transfer | PermissionMask.Copy)) != + (uint)(PermissionMask.Transfer | PermissionMask.Copy)) + { + remoteClient.SendAgentAlertMessage("Item must be have Copy and Transfer rights to attach to group notice", false); + return; + } + hasAttachment = true; + } + } + } + + if (hasAttachment) + { + //Bucket contains information about attachment. + // + //Byte offset and description of bucket data: + //0: 1 byte indicating if attachment is present + //1: 1 byte indicating the type of attachment + //2: 16 bytes - Group UUID + //18: 16 bytes - UUID of the attachment owner + //34: 16 bytes - UUID of the attachment + //50: variable - Name of the attachment + //??: NUL byte to terminate the attachment name + byte[] name = Encoding.UTF8.GetBytes(item.Name); + bucket = new byte[51 + name.Length];//2 bytes, 3 UUIDs, and name and null + bucket[0] = 1; //Has attachment flag + bucket[1] = (byte)item.AssetType; //Type of Attachment + GroupID.ToBytes(bucket, 2); + ownerID.ToBytes(bucket, 18); + itemID.ToBytes(bucket, 34); + name.CopyTo(bucket, 50); } else + { + bucket = new byte[19]; + bucket[0] = 0; //Has attachment flag + bucket[1] = 0; //Type of attachment + GroupID.ToBytes(bucket, 2); + bucket[18] = 0; //NUL terminate name of attachment + } + + UUID NoticeID = UUID.Random(); + + m_groupData.AddGroupNotice(remoteAgentID, GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); + OnNewGroupNotice?.Invoke(GroupID, NoticeID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID)) + { + if (m_debugEnabled) + { + UserAccount targetUser = m_sceneList[0].UserAccountService.GetUserAccount( + remoteClient.Scene.RegionInfo.ScopeID, member.AgentID); + + if (targetUser != null) + { + m_log.DebugFormat( + "[xmlGROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", + NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices); + } + else + { + m_log.DebugFormat( + "[xmlGROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", + NoticeID, member.AgentID, member.AcceptNotices); + } + } + } + } + + if (m_groupsMessagingModule != null) + { + GridInstantMessage msg + = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)InstantMessageDialog.GroupNotice); + + m_groupsMessagingModule.SendMessageToGroup( + msg, GroupID, remoteAgentID, gmd => gmd.AcceptNotices); + } + } + + else if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) + { + UUID noticeID = new UUID(im.imSessionID); + + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: Accepted notice {0} for {1}", noticeID, remoteClient.AgentId); + + if (noticeID == UUID.Zero) + return; + + UUID folderID = UUID.Zero; + try + { + if (im.binaryBucket != null && im.binaryBucket.Length >= 16) + folderID = new UUID(im.binaryBucket, 0); + } + catch + { + m_log.DebugFormat("[xmlGROUPS]: GroupNoticeInventoryAccepted failed to decode target folder"); + return; + } + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteAgentID, noticeID); + if (notice == null) { if (m_debugEnabled) m_log.DebugFormat( "[GROUPS]: Could not find notice {0} for {1} on GroupNoticeInventoryAccepted.", noticeID, remoteClient.AgentId); + return; } + + UUID giver = UUID.Zero; + UUID attachmentUUID = UUID.Zero; + try + { + giver = new UUID(notice.BinaryBucket, 18); + attachmentUUID = new UUID(notice.BinaryBucket, 34); + } + catch + { + m_log.DebugFormat("[xmlGROUPS]: GroupNoticeInventoryAccepted failed to giver or attachment ID"); + return; + } + + if (m_debugEnabled) + m_log.DebugFormat("[xmlGroups]: Giving inventory from {0} to {1}", giver, remoteAgentID); + + string message = "Could not find group notice attached item"; + + InventoryItemBase itemCopy = scene.InventoryService.GetItem(giver, attachmentUUID); + if (itemCopy != null) + { + itemCopy = scene.GiveInventoryItem(remoteAgentID, giver, attachmentUUID, folderID, out message); + } + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage(message, false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + } + + else if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryDeclined) + { + UUID noticeID = new UUID(im.imSessionID); + + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: Accepted notice {0} for {1}", noticeID, remoteAgentID); + + if (noticeID == UUID.Zero) + return; + + InventoryFolderBase trash = scene.InventoryService.GetFolderForType(remoteAgentID, FolderType.Trash); + if(trash == null) + { + m_log.DebugFormat("[xmlGROUPS]: failed to find trash folder for {0} ", remoteAgentID); + return; + } + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteAgentID, noticeID); + if (notice == null) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[GROUPS]: Could not find notice {0} for {1} on GroupNoticeInventoryAccepted.", + noticeID, remoteClient.AgentId); + return; + } + + UUID giver = UUID.Zero; + UUID attachmentUUID = UUID.Zero; + try + { + giver = new UUID(notice.BinaryBucket, 18); + attachmentUUID = new UUID(notice.BinaryBucket, 34); + } + catch + { + m_log.DebugFormat("[xmlGROUPS]: GroupNoticeInventoryAccepted failed to giver or attachment ID"); + return; + } + + if (m_debugEnabled) + m_log.DebugFormat("[xmlGroups]: Deny inventory from {0} to {1}", giver, remoteAgentID); + + string message = String.Empty; + + // check if we already have it + InventoryItemBase itemCopy = scene.InventoryService.GetItem(remoteAgentID, attachmentUUID); + if (itemCopy == null) + return; + + if (itemCopy.Folder == trash.ID || remoteAgentID == giver) + return; + + itemCopy.Folder = trash.ID; + scene.InventoryService.MoveItems(itemCopy.Owner, new List() { itemCopy }); + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage(message, false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); } // Interop, received special 210 code for ejecting a group member @@ -637,7 +746,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // TODO:FIXME: Use a presence server of some kind to find out where the // client actually is, and try contacting that region directly to notify them, // or provide the notification via xmlrpc update queue - if ((im.dialog == 210)) + else if ((im.dialog == 210)) { // This is sent from the region that the ejectee was ejected from // if it's being delivered here, then the ejectee is here @@ -660,7 +769,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private void OnGridInstantMessage(GridInstantMessage msg) { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + if (m_debugEnabled) + m_log.InfoFormat("[xmlGROUPS]: OnGridInstantMessage called"); // Trigger the above event handler OnInstantMessage(null, msg); @@ -1012,22 +1122,25 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + if (m_debugEnabled) + m_log.DebugFormat("[xmlGROUPS]: GroupNoticeRequest {0}", groupNoticeID); - GroupNoticeInfo data = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), groupNoticeID); + GroupNoticeInfo info = m_groupData.GetGroupNotice(remoteClient.AgentId, groupNoticeID); + if(info == null) + return; - if (data != null) - { - GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); - - OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); - } + GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested, info); + OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); } public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID, groupNoticeID); + return CreateGroupNoticeIM(agentID, groupNoticeID, dialog, info); + } + private GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog, GroupNoticeInfo info) + { GridInstantMessage msg = new GridInstantMessage(); byte[] bucket; @@ -1040,7 +1153,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups msg.Position = Vector3.Zero; msg.RegionID = UUID.Zero.Guid; - GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID, groupNoticeID); if (info != null) { msg.fromAgentID = info.GroupID.Guid; @@ -1053,13 +1165,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups //32 is due to not needing space for two of the UUIDs. //(Don't need UUID of attachment or its owner in IM) //50 offset gets us to start of attachment name. - //We are skipping the attachment flag, type, and - //the three UUID fields at the start of the bucket. - bucket = new byte[info.BinaryBucket.Length-32]; - bucket[0] = 1; //Has attachment - bucket[1] = info.BinaryBucket[1]; + bucket = new byte[info.BinaryBucket.Length - 32]; + Array.Copy(info.BinaryBucket, 0, + bucket, 0, 18); Array.Copy(info.BinaryBucket, 50, - bucket, 18, info.BinaryBucket.Length-50); + bucket, 18, info.BinaryBucket.Length - 50); } else { @@ -1069,7 +1179,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups bucket[18] = 0; //NUL terminate name } - info.GroupID.ToBytes(bucket, 2); msg.binaryBucket = bucket; } else