/* * 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 OpenSim 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 System.Collections; //using Nwc.XmlRpc; using log4net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Region.CoreModules.Framework.EventQueue; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using Caps = OpenSim.Framework.Communications.Capabilities.Caps; using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { public class XmlRpcGroupsModule : INonSharedRegionModule, IGroupsModule { /// /// ; To use this module, you must specify the following in your OpenSim.ini /// [GROUPS] /// Enabled = true /// Module = XmlRpcGroups /// XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php /// XmlRpcMessagingEnabled = true /// XmlRpcNoticesEnabled = true /// XmlRpcDebugEnabled = 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: /// ; System.Net.WebException: The request was aborted: The request was canceled. /// /// XmlRpcDisableKeepAlive = false /// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private List m_sceneList = new List(); // This only works when running as non-Shared, in shared, there may be multiple IClientAPIs for a single client private Dictionary m_activeClients = new Dictionary(); private IMessageTransferModule m_msgTransferModule = null; private IGroupDataProvider m_groupData = null; // Configuration settings private const string m_defaultXmlRpcServiceURL = "http://osflotsam.org/xmlrpc.php"; private bool m_groupsEnabled = false; private bool m_groupNoticesEnabled = true; private bool m_debugEnabled = true; #region IRegionModule Members public void Initialise(IConfigSource config) { IConfig groupsConfig = config.Configs["Groups"]; m_log.Info("[GROUPS]: Initializing XmlRpcGroups"); if (groupsConfig == null) { // Do not run this module by default. m_log.Info("[GROUPS]: No config found in OpenSim.ini -- not enabling XmlRpcGroups"); return; } else { m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); if (!m_groupsEnabled) { m_log.Info("[GROUPS]: Groups disabled in configuration"); return; } if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") { m_log.Info("[GROUPS]: Config Groups Module not set to XmlRpcGroups"); m_groupsEnabled = false; return; } string ServiceURL = groupsConfig.GetString("XmlRpcServiceURL", m_defaultXmlRpcServiceURL); bool DisableKeepAlive = groupsConfig.GetBoolean("XmlRpcDisableKeepAlive", false); m_groupData = new XmlRpcGroupDataProvider(ServiceURL, DisableKeepAlive); m_log.InfoFormat("[GROUPS]: XmlRpc Service URL set to: {0}", ServiceURL); m_groupNoticesEnabled = groupsConfig.GetBoolean("XmlRpcNoticesEnabled", true); m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); } } public void AddRegion(Scene scene) { if (m_groupsEnabled) scene.RegisterModuleInterface(this); } public void RegionLoaded(Scene scene) { if (!m_groupsEnabled) return; if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_msgTransferModule = scene.RequestModuleInterface(); // No message transfer module, no notices, group invites, rejects, ejects, etc if (m_msgTransferModule == null) { m_groupsEnabled = false; m_log.Info("[GROUPS]: Could not get MessageTransferModule"); Close(); return; } m_sceneList.Add(scene); scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnClientClosed += OnClientClosed; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; } public void RemoveRegion(Scene scene) { if (!m_groupsEnabled) return; if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_sceneList.Remove(scene); } public void Close() { if (!m_groupsEnabled) return; m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module."); } public string Name { get { return "XmlRpcGroupsModule"; } } #endregion private void UpdateAllClientsWithGroupInfo() { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); foreach (IClientAPI client in m_activeClients.Values) { UpdateClientWithGroupInfo(client); } } private void UpdateClientWithGroupInfo(IClientAPI client) { m_log.InfoFormat("[GROUPS] {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, client.Name); OnAgentDataUpdateRequest(client, client.AgentId, UUID.Zero); // Need to send a group membership update to the client // UDP version doesn't seem to behave nicely // client.SendGroupMembership(GetMembershipData(client.AgentId)); GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(client.AgentId).ToArray(); SendGroupMembershipInfoViaCaps(client, membershipData); client.SendAvatarGroupsReply(client.AgentId, membershipData); } #region EventHandlers private void OnNewClient(IClientAPI client) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); lock (m_activeClients) { if (!m_activeClients.ContainsKey(client.AgentId)) { client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; client.OnDirFindQuery += OnDirFindQuery; client.OnInstantMessage += OnInstantMessage; m_activeClients.Add(client.AgentId, client); } } UpdateClientWithGroupInfo(client); } private void OnClientClosed(UUID agentId) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); lock (m_activeClients) { if (m_activeClients.ContainsKey(agentId)) { IClientAPI client = m_activeClients[agentId]; client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest; client.OnDirFindQuery -= OnDirFindQuery; client.OnInstantMessage -= OnInstantMessage; m_activeClients.Remove(agentId); } else { m_log.InfoFormat("[GROUPS] Client closed that wasn't registered here."); } } } void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) { if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) { m_log.InfoFormat("[GROUPS] {0} called with queryText({1}) queryFlags({2}) queryStart({3})", System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(queryText).ToArray()); } } private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID agentID, UUID sessionID) { m_log.InfoFormat("[GROUPS] {0} called with SessionID :: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, sessionID); UUID activeGroupID = UUID.Zero; string activeGroupTitle = string.Empty; string activeGroupName = string.Empty; ulong activeGroupPowers = (ulong)GroupPowers.None; GroupMembershipData membership = m_groupData.GetAgentActiveMembership(agentID); if (membership != null) { activeGroupID = membership.GroupID; activeGroupTitle = membership.GroupTitle; activeGroupPowers = membership.GroupPowers; } string firstname, lastname; IClientAPI agent; if (m_activeClients.TryGetValue(agentID, out agent)) { firstname = agent.FirstName; lastname = agent.LastName; } else { firstname = "Unknown"; lastname = "Unknown"; } UpdateScenePresenceWithTitle(agentID, activeGroupTitle); remoteClient.SendAgentDataUpdate(agentID, activeGroupID, firstname, lastname, activeGroupPowers, activeGroupName, activeGroupTitle); } private void HandleUUIDGroupNameRequest(UUID groupID, IClientAPI remoteClient) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); string groupName; GroupRecord group = m_groupData.GetGroupRecord(groupID, null); if (group != null) { groupName = group.GroupName; } else { groupName = "Unknown"; } remoteClient.SendGroupNameReply(groupID, groupName); } private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Group invitations if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) { m_log.WarnFormat("[GROUPS] Received an IIM for {0}.", ((InstantMessageDialog)im.dialog).ToString()); UUID inviteID = new UUID(im.imSessionID); GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(inviteID); m_log.WarnFormat("[GROUPS] Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); UUID fromAgentID = new UUID(im.fromAgentID); if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) { // Accept if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) { m_log.WarnFormat("[GROUPS] Received an accept invite notice."); // and the sessionid is the role m_groupData.AddAgentToGroup(inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); if (m_msgTransferModule != null) { GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; msg.fromAgentID = UUID.Zero.Guid; msg.toAgentID = inviteInfo.AgentID.Guid; msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.fromAgentName = "Groups"; msg.message = string.Format("You have been added to the group."); msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox; msg.fromGroup = false; msg.offline = (byte)0; msg.ParentEstateID = 0; msg.Position = Vector3.Zero; msg.RegionID = UUID.Zero.Guid; msg.binaryBucket = new byte[0]; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); } UpdateAllClientsWithGroupInfo(); m_groupData.RemoveAgentToGroupInvite(inviteID); } // Reject if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) { m_log.WarnFormat("[GROUPS] Received a reject invite notice."); m_groupData.RemoveAgentToGroupInvite(inviteID); } } } // Group notices if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) { if (!m_groupNoticesEnabled) { return; } UUID groupID = new UUID(im.toAgentID); if (m_groupData.GetGroupRecord(groupID, null) != null) { UUID noticeID = UUID.Random(); string subject = im.message.Substring(0, im.message.IndexOf('|')); string message = im.message.Substring(subject.Length + 1); 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 { string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); binBucket = binBucket.Remove(0, 14).Trim(); 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) { m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); } // 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 } m_groupData.AddGroupNotice(groupID, noticeID, im.fromAgentName, subject, message, bucket); if (OnNewGroupNotice != null) { OnNewGroupNotice(groupID, noticeID); } // Build notice IIM GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, noticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); // Send notice out to everyone that wants notices foreach (GroupMembersData member in m_groupData.GetGroupMembers(groupID)) { if (member.AcceptNotices) { msg.toAgentID = member.AgentID.Guid; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) {}); } } } } // 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 // client actually is, and try contacting that region directly to notify them, // or provide the notification via xmlrpc update queue 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 // so we need to send local updates to the agent. if (m_msgTransferModule != null) { im.dialog = (byte)InstantMessageDialog.MessageFromAgent; m_msgTransferModule.SendInstantMessage(im, delegate(bool success) {}); } UUID ejecteeID = new UUID(im.toAgentID); UUID groupID = new UUID(im.toAgentID); if (m_activeClients.ContainsKey(ejecteeID)) { m_activeClients[ejecteeID].SendAgentDropGroup(groupID); } } } private void OnGridInstantMessage(GridInstantMessage msg) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Trigger the above event handler OnInstantMessage(null, msg); // If a message from a group arrives here, it may need to be forwarded to a local client if (msg.fromGroup == true) { switch( msg.dialog ) { case (byte)InstantMessageDialog.GroupInvitation: case (byte)InstantMessageDialog.GroupNotice: UUID toAgentID = new UUID(msg.toAgentID); if (m_activeClients.ContainsKey(toAgentID)) { m_activeClients[toAgentID].SendInstantMessage(msg); } break; } } } #endregion private void UpdateScenePresenceWithTitle(UUID agentID, string title) { m_log.DebugFormat("[GROUPS] Updating scene title for {0} with title: {1}", agentID, title); ScenePresence presence = null; lock (m_sceneList) { foreach (Scene scene in m_sceneList) { presence = scene.GetScenePresence(agentID); if (presence != null) { presence.Grouptitle = title; // FixMe: Ter suggests a "Schedule" method that I can't find. presence.SendFullUpdateToAllClients(); } } } } #region IGroupsModule Members public event NewGroupNotice OnNewGroupNotice; public GroupRecord GetGroupRecord(UUID groupID) { return m_groupData.GetGroupRecord(groupID, null); } public void ActivateGroup(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_groupData.SetAgentActiveGroup(remoteClient.AgentId, groupID); // UpdateClientWithGroupInfo(remoteClient); UpdateAllClientsWithGroupInfo(); } /// /// Get the Role Titles for an Agent, for a specific group /// public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List agentRoles = m_groupData.GetAgentGroupRoles(remoteClient.AgentId, groupID); GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID); List titles = new List(); foreach (GroupRolesData role in agentRoles) { GroupTitlesData title = new GroupTitlesData(); title.Name = role.Name; if (agentMembership != null) { title.Selected = agentMembership.ActiveRole == role.RoleID; } title.UUID = role.RoleID; titles.Add(title); } return titles; } public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List data = m_groupData.GetGroupMembers(groupID); foreach (GroupMembersData member in data) { m_log.InfoFormat("[GROUPS] {0} {1}", member.AgentID, member.Title); } return data; } public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List data = m_groupData.GetGroupRoles(groupID); foreach (GroupRolesData member in data) { m_log.InfoFormat("[GROUPS] {0} {1}", member.Title, member.Members); } return data; } public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List data = m_groupData.GetGroupRoleMembers(groupID); foreach (GroupRoleMembersData member in data) { m_log.InfoFormat("[GROUPS] Av: {0} Role: {1}", member.MemberID, member.RoleID); } return data; } public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GroupProfileData profile = new GroupProfileData(); GroupRecord groupInfo = m_groupData.GetGroupRecord(groupID, null); if (groupInfo != null) { profile.AllowPublish = groupInfo.AllowPublish; profile.Charter = groupInfo.Charter; profile.FounderID = groupInfo.FounderID; profile.GroupID = groupID; profile.GroupMembershipCount = m_groupData.GetGroupMembers(groupID).Count; profile.GroupRolesCount = m_groupData.GetGroupRoles(groupID).Count; profile.InsigniaID = groupInfo.GroupPicture; profile.MaturePublish = groupInfo.MaturePublish; profile.MembershipFee = groupInfo.MembershipFee; profile.Money = 0; // TODO: Get this from the currency server? profile.Name = groupInfo.GroupName; profile.OpenEnrollment = groupInfo.OpenEnrollment; profile.OwnerRole = groupInfo.OwnerRoleID; profile.ShowInList = groupInfo.ShowInList; } GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID); if (memberInfo != null) { profile.MemberTitle = memberInfo.GroupTitle; profile.PowersMask = memberInfo.GroupPowers; } return profile; } public GroupMembershipData[] GetMembershipData(UUID userID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); return m_groupData.GetAgentGroupMemberships(userID).ToArray(); } public GroupMembershipData GetMembershipData(UUID groupID, UUID userID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); return m_groupData.GetAgentGroupMembership(userID, groupID); } public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: Security Check? m_groupData.UpdateGroup(groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); } public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) { // TODO: Security Check? if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_groupData.SetAgentGroupInfo(remoteClient.AgentId, groupID, acceptNotices, listInProfile); } public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_groupData.GetGroupRecord(UUID.Zero, name) != null) { remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); return UUID.Zero; } UUID groupID = m_groupData.CreateGroup(name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId); remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); UpdateClientWithGroupInfo(remoteClient); return groupID; } public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // ToDo: check if agent is a member of group and is allowed to see notices? return m_groupData.GetGroupNotices(groupID).ToArray(); } /// /// Get the title of the agent's current role. /// public string GetGroupTitle(UUID avatarID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GroupMembershipData membership = m_groupData.GetAgentActiveMembership(avatarID); if (membership != null) { return membership.GroupTitle; } return string.Empty; } /// /// Change the current Active Group Role for Agent /// public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_groupData.SetAgentActiveGroupRole(remoteClient.AgentId, groupID, titleRoleID); UpdateAllClientsWithGroupInfo(); } public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: Security Checks? switch ((OpenMetaverse.GroupRoleUpdate)updateType) { case OpenMetaverse.GroupRoleUpdate.Create: m_groupData.AddGroupRole(groupID, UUID.Random(), name, description, title, powers); break; case OpenMetaverse.GroupRoleUpdate.Delete: m_groupData.RemoveGroupRole(groupID, roleID); break; case OpenMetaverse.GroupRoleUpdate.UpdateAll: case OpenMetaverse.GroupRoleUpdate.UpdateData: case OpenMetaverse.GroupRoleUpdate.UpdatePowers: m_groupData.UpdateGroupRole(groupID, roleID, name, description, title, powers); break; case OpenMetaverse.GroupRoleUpdate.NoUpdate: default: // No Op break; } UpdateClientWithGroupInfo(remoteClient); } public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Todo: Security check switch (changes) { case 0: // Add m_groupData.AddAgentToGroupRole(memberID, groupID, roleID); break; case 1: // Remove m_groupData.RemoveAgentFromGroupRole(memberID, groupID, roleID); break; default: m_log.ErrorFormat("[GROUPS] {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); break; } UpdateClientWithGroupInfo(remoteClient); } public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GroupNoticeInfo data = m_groupData.GetGroupNotice(groupNoticeID); if (data != null) { if (m_msgTransferModule != null) { GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; msg.fromAgentID = data.GroupID.Guid; msg.toAgentID = remoteClient.AgentId.Guid; msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.fromAgentName = "Group Notice From"; 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; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); } } } public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) { m_log.WarnFormat("[GROUPS] {0} is probably not properly implemented", System.Reflection.MethodBase.GetCurrentMethod().Name); GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; msg.toAgentID = agentID.Guid; msg.dialog = dialog; // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; msg.fromGroup = true; msg.offline = (byte)0; msg.ParentEstateID = 0; msg.Position = Vector3.Zero; msg.RegionID = UUID.Zero.Guid; GroupNoticeInfo info = m_groupData.GetGroupNotice(groupNoticeID); if (info != null) { msg.fromAgentID = info.GroupID.Guid; msg.timestamp = info.noticeData.Timestamp; msg.fromAgentName = info.noticeData.FromName; msg.message = info.noticeData.Subject + "|" + info.Message; msg.binaryBucket = info.BinaryBucket; } return msg; } public void SendAgentGroupDataUpdate(IClientAPI remoteClient) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); UpdateClientWithGroupInfo(remoteClient); } public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Should check to see if OpenEnrollment, or if there's an outstanding invitation m_groupData.AddAgentToGroup(remoteClient.AgentId, groupID, UUID.Zero); remoteClient.SendJoinGroupReply(groupID, true); UpdateClientWithGroupInfo(remoteClient); } public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_groupData.RemoveAgentFromGroup(remoteClient.AgentId, groupID); remoteClient.SendLeaveGroupReply(groupID, true); remoteClient.SendAgentDropGroup(groupID); UpdateClientWithGroupInfo(remoteClient); } public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Todo: Security check? m_groupData.RemoveAgentFromGroup(ejecteeID, groupID); remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true); if (m_msgTransferModule != null) { GroupRecord groupInfo = m_groupData.GetGroupRecord(groupID, null); UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(ejecteeID); if ((groupInfo == null) || (userProfile == null)) { return; } // Send Message to Ejectee GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; msg.fromAgentID = remoteClient.AgentId.Guid; // msg.fromAgentID = info.GroupID; msg.toAgentID = ejecteeID.Guid; //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.timestamp = 0; msg.fromAgentName = remoteClient.Name; msg.message = string.Format("You have been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName); msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; msg.fromGroup = false; msg.offline = (byte)0; msg.ParentEstateID = 0; msg.Position = Vector3.Zero; msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; msg.binaryBucket = new byte[0]; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success,ToString()); }); // 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 // client actually is, and try contacting that region directly to notify them, // or provide the notification via xmlrpc update queue msg = new GridInstantMessage(); msg.imSessionID = UUID.Zero.Guid; msg.fromAgentID = remoteClient.AgentId.Guid; msg.toAgentID = remoteClient.AgentId.Guid; msg.timestamp = 0; msg.fromAgentName = remoteClient.Name; if (userProfile != null) { msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, userProfile.Name); } else { msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, "Unknown member"); } msg.dialog = (byte)210; //interop msg.fromGroup = false; msg.offline = (byte)0; msg.ParentEstateID = 0; msg.Position = Vector3.Zero; msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; msg.binaryBucket = new byte[0]; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success, ToString()); }); } UpdateAllClientsWithGroupInfo(); } public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); m_log.WarnFormat("[GROUPS] GID {0}, AID {1}, RID {2} ", groupID, invitedAgentID, roleID); // Todo: Security check, probably also want to send some kind of notification UUID inviteID = UUID.Random(); m_log.WarnFormat("[GROUPS] Invite ID: {0}", inviteID); m_groupData.AddAgentToGroupInvite(inviteID, groupID, roleID, invitedAgentID); if (m_msgTransferModule != null) { Guid inviteUUID = inviteID.Guid; GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = inviteUUID; // msg.fromAgentID = remoteClient.AgentId.Guid; msg.fromAgentID = groupID.Guid; msg.toAgentID = invitedAgentID.Guid; //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.timestamp = 0; msg.fromAgentName = remoteClient.Name; msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", remoteClient.Name); msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; msg.fromGroup = true; msg.offline = (byte)0; msg.ParentEstateID = 0; msg.Position = Vector3.Zero; msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; msg.binaryBucket = new byte[20]; m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success,ToString()); }); } } #endregion void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, GroupMembershipData[] data) { if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDArray agentData = new OSDArray(1); OSDMap agentDataMap = new OSDMap(1); agentDataMap.Add("AgentID", OSD.FromUUID(remoteClient.AgentId)); agentData.Add(agentDataMap); OSDArray groupData = new OSDArray(data.Length); OSDArray newGroupData = new OSDArray(data.Length); foreach (GroupMembershipData membership in data) { OSDMap groupDataMap = new OSDMap(6); OSDMap newGroupDataMap = new OSDMap(1); groupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); groupDataMap.Add("GroupPowers", OSD.FromBinary(membership.GroupPowers)); groupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices)); groupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture)); groupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution)); groupDataMap.Add("GroupName", OSD.FromString(membership.GroupName)); newGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile)); groupData.Add(groupDataMap); newGroupData.Add(newGroupDataMap); } OSDMap llDataStruct = new OSDMap(3); llDataStruct.Add("AgentData", agentData); llDataStruct.Add("GroupData", groupData); llDataStruct.Add("NewGroupData", newGroupData); IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); if (queue != null) { queue.Enqueue(EventQueueHelper.buildEvent("AgentGroupDataUpdate", llDataStruct), remoteClient.AgentId); } } } }