diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index 29f95919b2..a8446605ce 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -39,6 +39,7 @@ using OpenSim.Framework.Communications; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; +using System.Text; using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups @@ -50,19 +51,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups /// ; To use this module, you must specify the following in your OpenSim.ini /// [GROUPS] /// Enabled = true - /// + /// /// Module = GroupsModule /// NoticesEnabled = true /// DebugEnabled = true - /// + /// /// GroupsServicesConnectorModule = XmlRpcGroupsServicesConnector /// XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php /// XmlRpcServiceReadKey = 1234 /// XmlRpcServiceWriteKey = 1234 - /// + /// /// MessagingModule = GroupsMessagingModule /// MessagingEnabled = true - /// + /// /// ; Disables HTTP Keep-Alive for Groups Module HTTP Requests, work around for /// ; a problem discovered on some Windows based region servers. Only disable /// ; if you see a large number (dozens) of the following Exceptions: @@ -77,7 +78,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private List m_sceneList = new List(); private IMessageTransferModule m_msgTransferModule = null; - + private IGroupsServicesConnector m_groupData = null; // Configuration settings @@ -195,10 +196,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - // The InstantMessageModule itself doesn't do this, + // The InstantMessageModule itself doesn't do this, // so lets see if things explode if we don't do it // scene.EventManager.OnClientClosed += OnClientClosed; - } public void RemoveRegion(Scene scene) @@ -222,7 +222,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module."); } - public Type ReplaceableInterface + public Type ReplaceableInterface { get { return null; } } @@ -274,7 +274,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections. * The OnClientClosed event does not provide anything to indicate which one of those should be closed * nor does it provide what scene it was from so that the specific reference can be looked up. - * The InstantMessageModule.cs does not currently worry about unregistering the handles, + * The InstantMessageModule.cs does not currently worry about unregistering the handles, * and it should be an issue, since it's the client that references us not the other way around * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed private void OnClientClosed(UUID AgentId) @@ -297,8 +297,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here."); } - - } } */ @@ -307,15 +305,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) { - if (m_debugEnabled) + if (m_debugEnabled) m_log.DebugFormat( - "[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", + "[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); // TODO: This currently ignores pretty much all the query flags including Mature and sort order remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentID(remoteClient), queryText).ToArray()); } - + } private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) @@ -345,7 +343,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); string GroupName; - + GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null); if (group != null) { @@ -407,7 +405,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups UpdateAllClientsWithGroupInfo(inviteInfo.AgentID); - // TODO: If the inviter is still online, they need an agent dataupdate + // TODO: If the inviter is still online, they need an agent dataupdate // and maybe group membership updates for the invitee m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID); @@ -437,44 +435,75 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups 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)) - { - bucket = new byte[19]; - bucket[0] = 0; //dunno - bucket[1] = 0; //dunno - GroupID.ToBytes(bucket, 2); - bucket[18] = 0; //dunno - } - else + if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) { string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); binBucket = binBucket.Remove(0, 14).Trim(); - if (m_debugEnabled) - { - m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); - OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); - - foreach (string key in binBucketOSD.Keys) + 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 = new InventoryItemBase(itemID, ownerID); + item = scene.InventoryService.GetItem(item); + + if (item != null) { - if (binBucketOSD.ContainsKey(key)) - { - m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); - } + //Got item details so include the attachment. + hasAttachment = true; } } - - // treat as if no attachment - bucket = new byte[19]; - bucket[0] = 0; //dunno - bucket[1] = 0; //dunno - GroupID.ToBytes(bucket, 2); - bucket[18] = 0; //dunno + 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) { @@ -484,22 +513,24 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Send notice out to everyone that wants notices foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID)) { - if (m_debugEnabled) + 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); + 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); + m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", + NoticeID, member.AgentID, member.AcceptNotices); } } - if (member.AcceptNotices) + if (member.AcceptNotices) { - // Build notice IIM + // Build notice IM GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); msg.toAgentID = member.AgentID.Guid; @@ -508,10 +539,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } } - + + if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) + { + //Is bucket large enough to hold UUID of the attachment? + if (im.binaryBucket.Length < 16) + return; + + UUID noticeID = new UUID(im.imSessionID); + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID); + if (notice != null) + { + 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); + + InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, + giver, attachmentUUID); + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + } + } + // Interop, received special 210 code for ejecting a group member // this only works within the comms servers domain, and won't work hypergrid - // TODO:FIXME: Use a presense server of some kind to find out where the + // TODO:FIXME: Use a presense 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)) @@ -574,7 +635,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { return m_groupData.GetGroupRecord(UUID.Zero, UUID.Zero, name); } - + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); @@ -582,7 +643,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups m_groupData.SetAgentActiveGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID); // Changing active group changes title, active powers, all kinds of things - // anyone who is in any region that can see this client, should probably be + // anyone who is in any region that can see this client, should probably be // updated with new group info. At a minimum, they should get ScenePresence // updated with new title. UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); @@ -618,10 +679,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) { - if (m_debugEnabled) + if (m_debugEnabled) m_log.DebugFormat( "[GROUPS]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name); - + List data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); if (m_debugEnabled) @@ -706,7 +767,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) { - if (m_debugEnabled) + if (m_debugEnabled) m_log.DebugFormat( "[GROUPS]: {0} called with groupID={1}, agentID={2}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID); @@ -796,7 +857,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (membership != null) { return membership.GroupTitle; - } + } return string.Empty; } @@ -812,7 +873,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // TODO: Not sure what all is needed here, but if the active group role change is for the group // the client currently has set active, then we need to do a scene presence update too // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID) - + UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); } @@ -870,7 +931,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups case 1: // Remove m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID); - + break; default: m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); @@ -889,26 +950,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (data != null) { - GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); - - GridInstantMessage msg = new GridInstantMessage(); - msg.imSessionID = UUID.Zero.Guid; - msg.fromAgentID = data.GroupID.Guid; - msg.toAgentID = GetRequestingAgentID(remoteClient).Guid; - msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); - msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName; - msg.message = data.noticeData.Subject + "|" + data.Message; - msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; - msg.fromGroup = true; - msg.offline = (byte)0; - msg.ParentEstateID = 0; - msg.Position = Vector3.Zero; - msg.RegionID = UUID.Zero.Guid; - msg.binaryBucket = data.BinaryBucket; + GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); } - } public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) @@ -916,10 +961,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GridInstantMessage msg = new GridInstantMessage(); - msg.imSessionID = UUID.Zero.Guid; + byte[] bucket; + + msg.imSessionID = groupNoticeID.Guid; msg.toAgentID = agentID.Guid; msg.dialog = dialog; - // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; msg.fromGroup = true; msg.offline = (byte)0; msg.ParentEstateID = 0; @@ -933,13 +979,38 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups msg.timestamp = info.noticeData.Timestamp; msg.fromAgentName = info.noticeData.FromName; msg.message = info.noticeData.Subject + "|" + info.Message; - msg.binaryBucket = info.BinaryBucket; + + if (info.BinaryBucket[0] > 0) + { + //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]; + Array.Copy(info.BinaryBucket, 50, + bucket, 18, info.BinaryBucket.Length-50); + } + else + { + bucket = new byte[19]; + bucket[0] = 0; //No attachment + bucket[1] = 0; //Attachment type + bucket[18] = 0; //NUL terminate name + } + + info.GroupID.ToBytes(bucket, 2); + msg.binaryBucket = bucket; } else { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); + if (m_debugEnabled) + m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); + msg.fromAgentID = UUID.Zero.Guid; - msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.fromAgentName = string.Empty; msg.message = string.Empty; msg.binaryBucket = new byte[0]; @@ -1042,7 +1113,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Send Message to Ejectee GridInstantMessage msg = new GridInstantMessage(); - + msg.imSessionID = UUID.Zero.Guid; msg.fromAgentID = agentID.Guid; // msg.fromAgentID = info.GroupID; @@ -1063,7 +1134,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Message to ejector // Interop, received special 210 code for ejecting a group member // this only works within the comms servers domain, and won't work hypergrid - // TODO:FIXME: Use a presense server of some kind to find out where the + // TODO:FIXME: Use a presense 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 @@ -1232,7 +1303,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (!membership.ListInProfile) { - // If we're sending group info to remoteclient about another agent, + // If we're sending group info to remoteclient about another agent, // filter out groups the other agent doesn't want to share. continue; } @@ -1269,7 +1340,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient)); } - } private void SendScenePresenceUpdate(UUID AgentID, string Title) @@ -1373,7 +1443,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups membershipArray = membershipData.ToArray(); } } - + if (m_debugEnabled) { m_log.InfoFormat("[GROUPS]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId); @@ -1386,7 +1456,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return membershipArray; } - + private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);