From 989517725d02d8a2d216e599856eb2625b454e25 Mon Sep 17 00:00:00 2001 From: Michael Cortez Date: Wed, 5 Aug 2009 11:15:53 -0700 Subject: Begin refactoring XmlRpcGroups to a more generic Groups module that allows for replaceable Groups Service Connectors. --- .../Avatar/XmlRpcGroups/GroupsMessagingModule.cs | 548 ++++++++ .../Avatar/XmlRpcGroups/GroupsModule.cs | 1335 ++++++++++++++++++++ .../Avatar/XmlRpcGroups/IGroupDataProvider.cs | 91 -- .../XmlRpcGroups/IGroupsServicesConnector.cs | 91 ++ .../Avatar/XmlRpcGroups/XmlRpcGroupData.cs | 916 -------------- .../Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs | 548 -------- .../Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs | 1331 ------------------- .../XmlRpcGroupsServicesConnectorModule.cs | 1007 +++++++++++++++ 8 files changed, 2981 insertions(+), 2886 deletions(-) create mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs delete mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs delete mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs delete mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs delete mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs new file mode 100644 index 0000000..8fbda28 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs @@ -0,0 +1,548 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; + + +using log4net; +using Nini.Config; + +using OpenMetaverse; +using 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.Capabilities.Caps; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + public class GroupsMessagingModule : ISharedRegionModule + { + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsModule m_groupsModule = null; + + // TODO: Move this off to the xmlrpc server + public Dictionary> m_agentsInGroupSession = new Dictionary>(); + public Dictionary> m_agentsDroppedSession = new Dictionary>(); + + + // Config Options + private bool m_groupMessagingEnabled = false; + private bool m_debugEnabled = true; + + #region IRegionModuleBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + { + // Do not run this module by default. + return; + } + else + { + if (!groupsConfig.GetBoolean("Enabled", false)) + { + return; + } + + if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") + { + m_groupMessagingEnabled = false; + + return; + } + + m_groupMessagingEnabled = groupsConfig.GetBoolean("XmlRpcMessagingEnabled", true); + + if (!m_groupMessagingEnabled) + { + return; + } + + m_log.Info("[GROUPS-MESSAGING]: Initializing XmlRpcGroupsMessaging"); + + m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); + } + + m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroupsMessaging starting up"); + + } + + public void AddRegion(Scene scene) + { + // NoOp + } + public void RegionLoaded(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupsModule = scene.RequestModuleInterface(); + + // No groups module, no groups messaging + if (m_groupsModule == null) + { + m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsModule, XmlRpcGroupsMessaging is now disabled."); + Close(); + m_groupMessagingEnabled = false; + return; + } + + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no groups messaging + if (m_msgTransferModule == null) + { + m_log.Error("[GROUPS-MESSAGING]: Could not get MessageTransferModule"); + Close(); + m_groupMessagingEnabled = false; + return; + } + + + m_sceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_sceneList.Remove(scene); + } + + public void Close() + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[GROUPS-MESSAGING]: Shutting down XmlRpcGroupsMessaging module."); + + foreach (Scene scene in m_sceneList) + { + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + } + + m_sceneList.Clear(); + + m_groupsModule = null; + m_msgTransferModule = null; + } + + public Type ReplacableInterface + { + get { return null; } + } + + public string Name + { + get { return "XmlRpcGroupsMessaging"; } + } + + #endregion + + #region ISharedRegionModule Members + + public void PostInitialise() + { + // NoOp + } + + #endregion + + #region SimGridEventHandlers + + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); + + client.OnInstantMessage += OnInstantMessage; + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + // The instant message module will only deliver messages of dialog types: + // MessageFromAgent, StartTyping, StopTyping, MessageFromObject + // + // Any other message type will not be delivered to a client by the + // Instant Message Module + + + if (m_debugEnabled) + { + m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(msg); + } + + // Incoming message from a group + if ((msg.fromGroup == true) && + ((msg.dialog == (byte)InstantMessageDialog.SessionSend) + || (msg.dialog == (byte)InstantMessageDialog.SessionAdd) + || (msg.dialog == (byte)InstantMessageDialog.SessionDrop))) + { + ProcessMessageFromGroupSession(msg); + } + } + + private void ProcessMessageFromGroupSession(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); + + switch (msg.dialog) + { + case (byte)InstantMessageDialog.SessionAdd: + AddAgentToGroupSession(msg.fromAgentID, msg.imSessionID); + break; + + case (byte)InstantMessageDialog.SessionDrop: + RemoveAgentFromGroupSession(msg.fromAgentID, msg.imSessionID); + break; + + case (byte)InstantMessageDialog.SessionSend: + if (!m_agentsInGroupSession.ContainsKey(msg.toAgentID) + && !m_agentsDroppedSession.ContainsKey(msg.toAgentID)) + { + // Agent not in session and hasn't dropped from session + // Add them to the session for now, and Invite them + AddAgentToGroupSession(msg.toAgentID, msg.imSessionID); + + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI activeClient = GetActiveClient(toAgentID); + if (activeClient != null) + { + UUID groupID = new UUID(msg.fromAgentID); + + GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); + if (groupInfo != null) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message"); + + // Force? open the group session dialog??? + IEventQueue eq = activeClient.Scene.RequestModuleInterface(); + eq.ChatterboxInvitation( + groupID + , groupInfo.GroupName + , new UUID(msg.fromAgentID) + , msg.message, new UUID(msg.toAgentID) + , msg.fromAgentName + , msg.dialog + , msg.timestamp + , msg.offline == 1 + , (int)msg.ParentEstateID + , msg.Position + , 1 + , new UUID(msg.imSessionID) + , msg.fromGroup + , Utils.StringToBytes(groupInfo.GroupName) + ); + + eq.ChatterBoxSessionAgentListUpdates( + new UUID(groupID) + , new UUID(msg.fromAgentID) + , new UUID(msg.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + } + else if (!m_agentsDroppedSession.ContainsKey(msg.toAgentID)) + { + // User hasn't dropped, so they're in the session, + // maybe we should deliver it. + IClientAPI client = GetActiveClient(new UUID(msg.toAgentID)); + if (client != null) + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name); + client.SendInstantMessage(msg); + } + else + { + m_log.WarnFormat("[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID); + } + } + break; + + default: + m_log.WarnFormat("[GROUPS-MESSAGING]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString()); + break; + } + } + + #endregion + + #region ClientEvents + + private void RemoveAgentFromGroupSession(Guid agentID, Guid sessionID) + { + if (m_agentsInGroupSession.ContainsKey(sessionID)) + { + // If in session remove + if (m_agentsInGroupSession[sessionID].Contains(agentID)) + { + m_agentsInGroupSession[sessionID].Remove(agentID); + } + + // If not in dropped list, add + if (!m_agentsDroppedSession[sessionID].Contains(agentID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Dropped {1} from session {0}", sessionID, agentID); + m_agentsDroppedSession[sessionID].Add(agentID); + } + } + } + + private void AddAgentToGroupSession(Guid agentID, Guid sessionID) + { + // Add Session Status if it doesn't exist for this session + CreateGroupSessionTracking(sessionID); + + // If nessesary, remove from dropped list + if (m_agentsDroppedSession[sessionID].Contains(agentID)) + { + m_agentsDroppedSession[sessionID].Remove(agentID); + } + + // If nessesary, add to in session list + if (!m_agentsInGroupSession[sessionID].Contains(agentID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Added {1} to session {0}", sessionID, agentID); + m_agentsInGroupSession[sessionID].Add(agentID); + } + } + + private void CreateGroupSessionTracking(Guid sessionID) + { + if (!m_agentsInGroupSession.ContainsKey(sessionID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Creating session tracking for : {0}", sessionID); + m_agentsInGroupSession.Add(sessionID, new List()); + m_agentsDroppedSession.Add(sessionID, new List()); + } + } + + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) + { + m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(im); + } + + // Start group IM session + if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) + { + UUID groupID = new UUID(im.toAgentID); + + GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); + if (groupInfo != null) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Start Group Session for {0}", groupInfo.GroupName); + + AddAgentToGroupSession(im.fromAgentID, im.imSessionID); + + ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, groupID); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + queue.ChatterBoxSessionAgentListUpdates( + new UUID(groupID) + , new UUID(im.fromAgentID) + , new UUID(im.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + + // Send a message from locally connected client to a group + if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) + { + UUID groupID = new UUID(im.toAgentID); + + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", groupID, im.imSessionID.ToString()); + + SendMessageToGroup(im, groupID); + } + } + + #endregion + + private void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + foreach (GroupMembersData member in m_groupsModule.GroupMembersRequest(null, groupID)) + { + if (m_agentsDroppedSession[im.imSessionID].Contains(member.AgentID.Guid)) + { + // Don't deliver messages to people who have dropped this session + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); + continue; + } + + // Copy Message + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = im.imSessionID; + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.offline = im.offline; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = im.binaryBucket; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + + // Updat Pertinate fields to make it a "group message" + msg.fromAgentID = groupID.Guid; + msg.fromGroup = true; + + msg.toAgentID = member.AgentID.Guid; + + IClientAPI client = GetActiveClient(member.AgentID); + if (client == null) + { + // If they're not local, forward across the grid + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + else + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); + ProcessMessageFromGroupSession(msg); + } + } + } + + void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + OSDMap moderatedMap = new OSDMap(4); + moderatedMap.Add("voice", OSD.FromBoolean(false)); + + OSDMap sessionMap = new OSDMap(4); + sessionMap.Add("moderated_mode", moderatedMap); + sessionMap.Add("session_name", OSD.FromString(groupName)); + sessionMap.Add("type", OSD.FromInteger(0)); + sessionMap.Add("voice_enabled", OSD.FromBoolean(false)); + + OSDMap bodyMap = new OSDMap(4); + bodyMap.Add("session_id", OSD.FromUUID(groupID)); + bodyMap.Add("temp_session_id", OSD.FromUUID(groupID)); + bodyMap.Add("success", OSD.FromBoolean(true)); + bodyMap.Add("session_info", sessionMap); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(EventQueueHelper.buildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); + } + } + + private void DebugGridInstantMessage(GridInstantMessage im) + { + // Don't log any normal IMs (privacy!) + if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent) + { + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False"); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID.ToString()); + m_log.WarnFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); + } + } + + #region Client Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + if (scene.Entities.ContainsKey(agentID) && + scene.Entities[agentID] is ScenePresence) + { + ScenePresence user = (ScenePresence)scene.Entities[agentID]; + if (!user.IsChildAgent) + { + return user.ControllingClient; + } + else + { + child = user.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + return child; + } + + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs new file mode 100644 index 0000000..f0e386b --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -0,0 +1,1335 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Timers; + +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.CoreModules.Framework.EventQueue; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +using Caps = OpenSim.Framework.Capabilities.Caps; +using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; + + + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + public class GroupsModule : ISharedRegionModule, 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 + /// XmlRpcServiceReadKey = 1234 + /// XmlRpcServiceWriteKey = 1234 + /// + /// ; 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(); + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + + class GroupRequestIDInfo + { + public GroupRequestID RequestID = new GroupRequestID(); + public DateTime LastUsedTMStamp = DateTime.MinValue; + } + private Dictionary m_clientRequestIDInfo = new Dictionary(); + private const int m_clientRequestIDFlushTimeOut = 300000; // Every 5 minutes + private Timer m_clientRequestIDFlushTimer = new Timer(); + + + // Configuration settings + private bool m_groupsEnabled = false; + private bool m_groupNoticesEnabled = true; + private bool m_debugEnabled = true; + + #region IRegionModuleBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + { + // Do not run this module by default. + return; + } + else + { + m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); + if (!m_groupsEnabled) + { + return; + } + + if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") + { + m_groupsEnabled = false; + + return; + } + + m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name); + + m_groupNoticesEnabled = groupsConfig.GetBoolean("XmlRpcNoticesEnabled", true); + m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); + + m_clientRequestIDFlushTimer.Interval = m_clientRequestIDFlushTimeOut; + m_clientRequestIDFlushTimer.Elapsed += FlushClientRequestIDInfoCache; + m_clientRequestIDFlushTimer.AutoReset = true; + m_clientRequestIDFlushTimer.Start(); + } + } + + void FlushClientRequestIDInfoCache(object sender, ElapsedEventArgs e) + { + lock (m_clientRequestIDInfo) + { + TimeSpan cacheTimeout = new TimeSpan(0,0, m_clientRequestIDFlushTimeOut / 1000); + UUID[] CurrentKeys = new UUID[m_clientRequestIDInfo.Count]; + foreach (UUID key in CurrentKeys) + { + if (DateTime.Now - m_clientRequestIDInfo[key].LastUsedTMStamp > cacheTimeout) + { + m_clientRequestIDInfo.Remove(key); + } + } + } + } + + 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + if (m_groupData == null) + { + m_groupData = scene.RequestModuleInterface(); + + // No Groups Service Connector, then nothing works... + if (m_groupData == null) + { + m_groupsEnabled = false; + m_log.Error("[GROUPS]: Could not get IGroupsServicesConnector"); + Close(); + return; + } + } + + if (m_msgTransferModule == null) + { + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no notices, group invites, rejects, ejects, etc + if (m_msgTransferModule == null) + { + m_groupsEnabled = false; + m_log.Error("[GROUPS]: Could not get MessageTransferModule"); + Close(); + return; + } + } + + lock (m_sceneList) + { + m_sceneList.Add(scene); + } + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + + // 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) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + lock (m_sceneList) + { + m_sceneList.Remove(scene); + } + } + + public void Close() + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module."); + + m_clientRequestIDFlushTimer.Stop(); + } + + public Type ReplacableInterface + { + get { return null; } + } + + public string Name + { + get { return "XmlRpcGroupsModule"; } + } + + #endregion + + #region ISharedRegionModule Members + + public void PostInitialise() + { + // NoOp + } + + #endregion + + #region EventHandlers + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; + client.OnDirFindQuery += OnDirFindQuery; + client.OnRequestAvatarProperties += OnRequestAvatarProperties; + + // Used for Notices and Group Invites/Accept/Reject + client.OnInstantMessage += OnInstantMessage; + + lock (m_clientRequestIDInfo) + { + if (m_clientRequestIDInfo.ContainsKey(client.AgentId)) + { + // flush any old RequestID information + m_clientRequestIDInfo.Remove(client.AgentId); + } + } + SendAgentGroupDataUpdate(client, client.AgentId); + } + + private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) + { + GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), avatarID).ToArray(); + remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups); + } + + /* + * This becomes very problematic in a shared module. In a shared module you may have more then one + * 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, + * 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) + { + if (m_debugEnabled) m_log.DebugFormat("[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 + { + if (m_debugEnabled) m_log.WarnFormat("[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) + { + if (m_debugEnabled) m_log.DebugFormat("[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(GetClientGroupRequestID(remoteClient), queryText).ToArray()); + } + + } + + private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + UUID activeGroupID = UUID.Zero; + string activeGroupTitle = string.Empty; + string activeGroupName = string.Empty; + ulong activeGroupPowers = (ulong)GroupPowers.None; + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetClientGroupRequestID(remoteClient), dataForAgentID); + if (membership != null) + { + activeGroupID = membership.GroupID; + activeGroupTitle = membership.GroupTitle; + activeGroupPowers = membership.GroupPowers; + } + + SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle); + + SendScenePresenceUpdate(dataForAgentID, activeGroupTitle); + } + + private void HandleUUIDGroupNameRequest(UUID GroupID,IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string GroupName; + + GroupRecord group = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Group invitations + if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) + { + UUID inviteID = new UUID(im.imSessionID); + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); + + if (inviteInfo == null) + { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: 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); + + UUID fromAgentID = new UUID(im.fromAgentID); + if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) + { + // Accept + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice."); + + // and the sessionid is the role + m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); + + 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]; + + OutgoingInstantMessage(msg, inviteInfo.AgentID); + + UpdateAllClientsWithGroupInfo(inviteInfo.AgentID); + + // TODO: If the inviter is still online, they need an agent dataupdate + // and maybe group membership updates for the invitee + + m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); + } + + // Reject + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); + } + } + } + + // Group notices + if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) + { + if (!m_groupNoticesEnabled) + { + return; + } + + UUID GroupID = new UUID(im.toAgentID); + if (m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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(); + 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) + { + 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(GetClientGroupRequestID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); + if (OnNewGroupNotice != null) + { + OnNewGroupNotice(GroupID, NoticeID); + } + + // Send notice out to everyone that wants notices + foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), GroupID)) + { + if (member.AcceptNotices) + { + // Build notice IIM + GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); + + msg.toAgentID = member.AgentID.Guid; + OutgoingInstantMessage(msg, member.AgentID); + } + } + } + } + + // 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. + + UUID ejecteeID = new UUID(im.toAgentID); + + im.dialog = (byte)InstantMessageDialog.MessageFromAgent; + OutgoingInstantMessage(im, ejecteeID); + + IClientAPI ejectee = GetActiveClient(ejecteeID); + if (ejectee != null) + { + UUID groupID = new UUID(im.fromAgentID); + ejectee.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); + IClientAPI localClient = GetActiveClient(toAgentID); + if (localClient != null) + { + localClient.SendInstantMessage(msg); + } + break; + } + } + } + + #endregion + + #region IGroupsModule Members + + public event NewGroupNotice OnNewGroupNotice; + + public GroupRecord GetGroupRecord(UUID GroupID) + { + return m_groupData.GetGroupRecord(null, GroupID, null); + } + + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, 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 + // updated with new group info. At a minimum, they should get ScenePresence + // updated with new title. + UpdateAllClientsWithGroupInfo(remoteClient.AgentId); + } + + /// + /// Get the Role Titles for an Agent, for a specific group + /// + public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + List agentRoles = m_groupData.GetAgentGroupRoles(grID, remoteClient.AgentId, groupID); + GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(grID, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), groupID); + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: {0} {1}", member.AgentID, member.Title); + } + } + + return data; + + } + + public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoles(GetClientGroupRequestID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRolesData member in data) + { + m_log.DebugFormat("[GROUPS]: {0} {1}", member.Title, member.Members); + } + } + + return data; + + } + + public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoleMembers(GetClientGroupRequestID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: Av: {0} Role: {1}", member.MemberID, member.RoleID); + } + } + + return data; + + + } + + public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupProfileData profile = new GroupProfileData(); + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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(grID, groupID).Count; + profile.GroupRolesCount = m_groupData.GetGroupRoles(grID, 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(grID, remoteClient.AgentId, groupID); + if (memberInfo != null) + { + profile.MemberTitle = memberInfo.GroupTitle; + profile.PowersMask = memberInfo.GroupPowers; + } + + return profile; + } + + public GroupMembershipData[] GetMembershipData(UUID agentID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMemberships(null, agentID).ToArray(); + } + + public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMembership(null, agentID, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Security Check? + + m_groupData.UpdateGroup(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentGroupInfo(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + if (m_groupData.GetGroupRecord(grID, UUID.Zero, name) != null) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); + return UUID.Zero; + } + // is there is a money module present ? + IMoneyModule money=remoteClient.Scene.RequestModuleInterface(); + if (money != null) + { + // do the transaction, that is if the agent has got sufficient funds + if (!money.GroupCreationCovered(remoteClient)) { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got issuficient funds to create a group."); + return UUID.Zero; + } + money.ApplyGroupCreationCharge(remoteClient.AgentId); + } + UUID groupID = m_groupData.CreateGroup(grID, name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId); + + remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); + + // Update the founder with new group information. + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + + return groupID; + } + + public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[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(GetClientGroupRequestID(remoteClient), groupID).ToArray(); + } + + /// + /// Get the title of the agent's current role. + /// + public string GetGroupTitle(UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(null, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroupRole(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, titleRoleID); + + // 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(remoteClient.AgentId).GroupID == GroupID) + + UpdateAllClientsWithGroupInfo(remoteClient.AgentId); + } + + + public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Security Checks? + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + switch ((OpenMetaverse.GroupRoleUpdate)updateType) + { + case OpenMetaverse.GroupRoleUpdate.Create: + m_groupData.AddGroupRole(grID, groupID, UUID.Random(), name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.Delete: + m_groupData.RemoveGroupRole(grID, groupID, roleID); + break; + + case OpenMetaverse.GroupRoleUpdate.UpdateAll: + case OpenMetaverse.GroupRoleUpdate.UpdateData: + case OpenMetaverse.GroupRoleUpdate.UpdatePowers: + m_groupData.UpdateGroupRole(grID, groupID, roleID, name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.NoUpdate: + default: + // No Op + break; + + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + } + + public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + // Todo: Security check + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + switch (changes) + { + case 0: + // Add + m_groupData.AddAgentToGroupRole(grID, memberID, groupID, roleID); + + break; + case 1: + // Remove + m_groupData.RemoveAgentFromGroupRole(grID, memberID, groupID, roleID); + + break; + default: + m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); + break; + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + } + + public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + GroupNoticeInfo data = m_groupData.GetGroupNotice(grID, groupNoticeID); + + if (data != null) + { + GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, data.GroupID, 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 : " + 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; + + OutgoingInstantMessage(msg, remoteClient.AgentId); + } + + } + + public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", 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)1; // Allow this message to be stored for offline use + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + + GroupNoticeInfo info = m_groupData.GetGroupNotice(null, 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; + } + else + { + 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.fromAgentName = string.Empty; + msg.message = string.Empty; + msg.binaryBucket = new byte[0]; + } + + return msg; + } + + public void SendAgentGroupDataUpdate(IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Send agent information about his groups + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + } + + public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Should check to see if OpenEnrollment, or if there's an outstanding invitation + m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, UUID.Zero); + + remoteClient.SendJoinGroupReply(groupID, true); + + // Should this send updates to everyone in the group? + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + } + + public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.RemoveAgentFromGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID); + + remoteClient.SendLeaveGroupReply(groupID, true); + + remoteClient.SendAgentDropGroup(groupID); + + // SL sends out notifcations to the group messaging session that the person has left + // Should this also update everyone who is in the group? + SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); + } + + public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRequestID grID = GetClientGroupRequestID(remoteClient); + + // Todo: Security check? + m_groupData.RemoveAgentFromGroup(grID, ejecteeID, groupID); + + remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, 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]; + OutgoingInstantMessage(msg, ejecteeID); + + + // 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]; + OutgoingInstantMessage(msg, remoteClient.AgentId); + + + // SL sends out messages to everyone in the group + // Who all should receive updates and what should they be updated with? + UpdateAllClientsWithGroupInfo(ejecteeID); + } + + public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + GroupRequestID grid = GetClientGroupRequestID(remoteClient); + + m_groupData.AddAgentToGroupInvite(grid, InviteID, groupID, roleID, invitedAgentID); + + // Check to see if the invite went through, if it did not then it's possible + // the remoteClient did not validate or did not have permission to invite. + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(grid, InviteID); + + if (inviteInfo != null) + { + 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]; + + OutgoingInstantMessage(msg, invitedAgentID); + } + } + } + + #endregion + + #region Client/Update Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + if (scene.Entities.ContainsKey(agentID) && + scene.Entities[agentID] is ScenePresence) + { + ScenePresence user = (ScenePresence)scene.Entities[agentID]; + if (!user.IsChildAgent) + { + return user.ControllingClient; + } + else + { + child = user.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + return child; + } + + private GroupRequestID GetClientGroupRequestID(IClientAPI client) + { + if (client == null) + { + return new GroupRequestID(); + } + + lock (m_clientRequestIDInfo) + { + if (!m_clientRequestIDInfo.ContainsKey(client.AgentId)) + { + GroupRequestIDInfo info = new GroupRequestIDInfo(); + info.RequestID.AgentID = client.AgentId; + info.RequestID.SessionID = client.SessionId; + + UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(client.AgentId); + if (userProfile == null) + { + // This should be impossible. If I've been passed a reference to a client + // that client should be registered with the UserService. So something + // is horribly wrong somewhere. + + m_log.WarnFormat("[GROUPS]: Could not find a user profile for {0} / {1}", client.Name, client.AgentId); + + // Default to local user service and hope for the best? + info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL; + + } + else if (userProfile is ForeignUserProfileData) + { + // They aren't from around here + ForeignUserProfileData fupd = (ForeignUserProfileData)userProfile; + info.RequestID.UserServiceURL = fupd.UserServerURI; + } + else + { + // They're a local user, use this: + info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL; + } + + m_clientRequestIDInfo.Add(client.AgentId, info); + } + + m_clientRequestIDInfo[client.AgentId].LastUsedTMStamp = DateTime.Now; + } + return m_clientRequestIDInfo[client.AgentId].RequestID; + } + + /// + /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'. + /// + private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, 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(dataForAgentID)); + 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); + } + + } + + private void SendScenePresenceUpdate(UUID AgentID, string Title) + { + if (m_debugEnabled) 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(); + } + } + } + } + + /// + /// Send updates to all clients who might be interested in groups data for dataForClientID + /// + private void UpdateAllClientsWithGroupInfo(UUID dataForClientID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Probably isn't nessesary to update every client in every scene. + // Need to examine client updates and do only what's nessesary. + lock (m_sceneList) + { + foreach (Scene scene in m_sceneList) + { + scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); }); + } + } + } + + /// + /// Update remoteClient with group information about dataForAgentID + /// + private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + + OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero); + + + // Need to send a group membership update to the client + // UDP version doesn't seem to behave nicely. But we're going to send it out here + // with an empty group membership to hopefully remove groups being displayed due + // to the core Groups Stub + remoteClient.SendGroupMembership(new GroupMembershipData[0]); + + GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), dataForAgentID).ToArray(); + + SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipData); + remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipData); + + } + + 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); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(dataForAgentID); + string firstname, lastname; + if (userProfile != null) + { + firstname = userProfile.FirstName; + lastname = userProfile.SurName; + } + else + { + firstname = "Unknown"; + lastname = "Unknown"; + } + + remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, + lastname, activeGroupPowers, activeGroupName, + activeGroupTitle); + } + + #endregion + + #region IM Backed Processes + + private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + IClientAPI localClient = GetActiveClient(msgTo); + if (localClient != null) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is local, delivering directly", localClient.Name); + localClient.SendInstantMessage(msg); + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Message Sent: {0}", success?"Succeeded":"Failed"); }); + } + } + + public void NotifyChange(UUID groupID) + { + // Notify all group members of a chnge in group roles and/or + // permissions + // + } + + #endregion + } + +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs deleted file mode 100644 index 302be4a..0000000 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 OpenMetaverse; - -using OpenSim.Framework; - -namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups -{ - interface IGroupDataProvider - { - UUID CreateGroup(GroupRequestID requestID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID); - void UpdateGroup(GroupRequestID requestID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); - GroupRecord GetGroupRecord(GroupRequestID requestID, UUID GroupID, string GroupName); - List FindGroups(GroupRequestID requestID, string search); - List GetGroupMembers(GroupRequestID requestID, UUID GroupID); - - void AddGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); - void UpdateGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); - void RemoveGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID); - List GetGroupRoles(GroupRequestID requestID, UUID GroupID); - List GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID); - - void AddAgentToGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); - void RemoveAgentFromGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID); - - void AddAgentToGroupInvite(GroupRequestID requestID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID); - GroupInviteInfo GetAgentToGroupInvite(GroupRequestID requestID, UUID inviteID); - void RemoveAgentToGroupInvite(GroupRequestID requestID, UUID inviteID); - - - void AddAgentToGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); - void RemoveAgentFromGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); - List GetAgentGroupRoles(GroupRequestID requestID, UUID AgentID, UUID GroupID); - - void SetAgentActiveGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID); - GroupMembershipData GetAgentActiveMembership(GroupRequestID requestID, UUID AgentID); - - void SetAgentActiveGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); - void SetAgentGroupInfo(GroupRequestID requestID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile); - - GroupMembershipData GetAgentGroupMembership(GroupRequestID requestID, UUID AgentID, UUID GroupID); - List GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID); - - void AddGroupNotice(GroupRequestID requestID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket); - GroupNoticeInfo GetGroupNotice(GroupRequestID requestID, UUID noticeID); - List GetGroupNotices(GroupRequestID requestID, UUID GroupID); - } - - public class GroupInviteInfo - { - public UUID GroupID = UUID.Zero; - public UUID RoleID = UUID.Zero; - public UUID AgentID = UUID.Zero; - public UUID InviteID = UUID.Zero; - } - - public class GroupRequestID - { - public UUID AgentID = UUID.Zero; - public string UserServiceURL = string.Empty; - public UUID SessionID = UUID.Zero; - } -} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs new file mode 100644 index 0000000..9e0fa2d --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs @@ -0,0 +1,91 @@ +/* + * 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 OpenMetaverse; + +using OpenSim.Framework; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + interface IGroupsServicesConnector + { + UUID CreateGroup(GroupRequestID requestID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID); + void UpdateGroup(GroupRequestID requestID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); + GroupRecord GetGroupRecord(GroupRequestID requestID, UUID GroupID, string GroupName); + List FindGroups(GroupRequestID requestID, string search); + List GetGroupMembers(GroupRequestID requestID, UUID GroupID); + + void AddGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void UpdateGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void RemoveGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID); + List GetGroupRoles(GroupRequestID requestID, UUID GroupID); + List GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID); + + void AddAgentToGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID); + + void AddAgentToGroupInvite(GroupRequestID requestID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID); + GroupInviteInfo GetAgentToGroupInvite(GroupRequestID requestID, UUID inviteID); + void RemoveAgentToGroupInvite(GroupRequestID requestID, UUID inviteID); + + + void AddAgentToGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); + List GetAgentGroupRoles(GroupRequestID requestID, UUID AgentID, UUID GroupID); + + void SetAgentActiveGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID); + GroupMembershipData GetAgentActiveMembership(GroupRequestID requestID, UUID AgentID); + + void SetAgentActiveGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID); + void SetAgentGroupInfo(GroupRequestID requestID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile); + + GroupMembershipData GetAgentGroupMembership(GroupRequestID requestID, UUID AgentID, UUID GroupID); + List GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID); + + void AddGroupNotice(GroupRequestID requestID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket); + GroupNoticeInfo GetGroupNotice(GroupRequestID requestID, UUID noticeID); + List GetGroupNotices(GroupRequestID requestID, UUID GroupID); + } + + public class GroupInviteInfo + { + public UUID GroupID = UUID.Zero; + public UUID RoleID = UUID.Zero; + public UUID AgentID = UUID.Zero; + public UUID InviteID = UUID.Zero; + } + + public class GroupRequestID + { + public UUID AgentID = UUID.Zero; + public string UserServiceURL = string.Empty; + public UUID SessionID = UUID.Zero; + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs deleted file mode 100644 index 1d4cde5..0000000 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs +++ /dev/null @@ -1,916 +0,0 @@ -/* - * 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; -using System.Collections.Generic; -using System.Reflection; -//using System.Text; - -using Nwc.XmlRpc; - -using log4net; -// using Nini.Config; - -using OpenMetaverse; -using OpenMetaverse.StructuredData; - -using OpenSim.Framework; -//using OpenSim.Region.Framework.Interfaces; - -namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups -{ - public class XmlRpcGroupDataProvider : IGroupDataProvider - { - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private string m_serviceURL = string.Empty; - - public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | - GroupPowers.Accountable | - GroupPowers.JoinChat | - GroupPowers.AllowVoiceChat | - GroupPowers.ReceiveNotices | - GroupPowers.StartProposal | - GroupPowers.VoteOnProposal; - - private bool m_disableKeepAlive = false; - - private string m_groupReadKey = string.Empty; - private string m_groupWriteKey = string.Empty; - - public XmlRpcGroupDataProvider(string serviceURL, bool disableKeepAlive, string groupReadKey, string groupWriteKey) - { - m_serviceURL = serviceURL.Trim(); - m_disableKeepAlive = disableKeepAlive; - - if ((serviceURL == null) || - (serviceURL == string.Empty)) - { - throw new Exception("Please specify a valid ServiceURL for XmlRpcGroupDataProvider in OpenSim.ini, [Groups], XmlRpcServiceURL"); - } - - m_groupReadKey = groupReadKey; - m_groupWriteKey = groupWriteKey; - } - - /// - /// Create a Group, including Everyone and Owners Role, place FounderID in both groups, select Owner as selected role, and newly created group as agent's active role. - /// - public UUID CreateGroup(GroupRequestID requestID, string name, string charter, bool showInList, UUID insigniaID, - int membershipFee, bool openEnrollment, bool allowPublish, - bool maturePublish, UUID founderID) - { - UUID GroupID = UUID.Random(); - UUID OwnerRoleID = UUID.Random(); - - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - param["Name"] = name; - param["Charter"] = charter; - param["ShowInList"] = showInList == true ? 1 : 0; - param["InsigniaID"] = insigniaID.ToString(); - param["MembershipFee"] = 0; - param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; - param["AllowPublish"] = allowPublish == true ? 1 : 0; - param["MaturePublish"] = maturePublish == true ? 1 : 0; - param["FounderID"] = founderID.ToString(); - param["EveryonePowers"] = ((ulong)m_DefaultEveryonePowers).ToString(); - param["OwnerRoleID"] = OwnerRoleID.ToString(); - - // Would this be cleaner as (GroupPowers)ulong.MaxValue; - GroupPowers OwnerPowers = GroupPowers.Accountable - | GroupPowers.AllowEditLand - | GroupPowers.AllowFly - | GroupPowers.AllowLandmark - | GroupPowers.AllowRez - | GroupPowers.AllowSetHome - | GroupPowers.AllowVoiceChat - | GroupPowers.AssignMember - | GroupPowers.AssignMemberLimited - | GroupPowers.ChangeActions - | GroupPowers.ChangeIdentity - | GroupPowers.ChangeMedia - | GroupPowers.ChangeOptions - | GroupPowers.CreateRole - | GroupPowers.DeedObject - | GroupPowers.DeleteRole - | GroupPowers.Eject - | GroupPowers.FindPlaces - | GroupPowers.Invite - | GroupPowers.JoinChat - | GroupPowers.LandChangeIdentity - | GroupPowers.LandDeed - | GroupPowers.LandDivideJoin - | GroupPowers.LandEdit - | GroupPowers.LandEjectAndFreeze - | GroupPowers.LandGardening - | GroupPowers.LandManageAllowed - | GroupPowers.LandManageBanned - | GroupPowers.LandManagePasses - | GroupPowers.LandOptions - | GroupPowers.LandRelease - | GroupPowers.LandSetSale - | GroupPowers.ModerateChat - | GroupPowers.ObjectManipulate - | GroupPowers.ObjectSetForSale - | GroupPowers.ReceiveNotices - | GroupPowers.RemoveMember - | GroupPowers.ReturnGroupOwned - | GroupPowers.ReturnGroupSet - | GroupPowers.ReturnNonGroup - | GroupPowers.RoleProperties - | GroupPowers.SendNotices - | GroupPowers.SetLandingPoint - | GroupPowers.StartProposal - | GroupPowers.VoteOnProposal; - param["OwnersPowers"] = ((ulong)OwnerPowers).ToString(); - - - - - Hashtable respData = XmlRpcCall(requestID, "groups.createGroup", param); - - if (respData.Contains("error")) - { - // UUID is not nullable - - return UUID.Zero; - } - - return UUID.Parse((string)respData["GroupID"]); - } - - public void UpdateGroup(GroupRequestID requestID, UUID groupID, string charter, bool showInList, - UUID insigniaID, int membershipFee, bool openEnrollment, - bool allowPublish, bool maturePublish) - { - Hashtable param = new Hashtable(); - param["GroupID"] = groupID.ToString(); - param["Charter"] = charter; - param["ShowInList"] = showInList == true ? 1 : 0; - param["InsigniaID"] = insigniaID.ToString(); - param["MembershipFee"] = membershipFee; - param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; - param["AllowPublish"] = allowPublish == true ? 1 : 0; - param["MaturePublish"] = maturePublish == true ? 1 : 0; - - XmlRpcCall(requestID, "groups.updateGroup", param); - } - - public void AddGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, - string title, ulong powers) - { - Hashtable param = new Hashtable(); - param["GroupID"] = groupID.ToString(); - param["RoleID"] = roleID.ToString(); - param["Name"] = name; - param["Description"] = description; - param["Title"] = title; - param["Powers"] = powers.ToString(); - - XmlRpcCall(requestID, "groups.addRoleToGroup", param); - } - - public void RemoveGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = groupID.ToString(); - param["RoleID"] = roleID.ToString(); - - XmlRpcCall(requestID, "groups.removeRoleFromGroup", param); - } - - public void UpdateGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, - string title, ulong powers) - { - Hashtable param = new Hashtable(); - param["GroupID"] = groupID.ToString(); - param["RoleID"] = roleID.ToString(); - if (name != null) - { - param["Name"] = name; - } - if (description != null) - { - param["Description"] = description; - } - if (title != null) - { - param["Title"] = title; - } - param["Powers"] = powers.ToString(); - - XmlRpcCall(requestID, "groups.updateGroupRole", param); - } - - public GroupRecord GetGroupRecord(GroupRequestID requestID, UUID GroupID, string GroupName) - { - Hashtable param = new Hashtable(); - if (GroupID != UUID.Zero) - { - param["GroupID"] = GroupID.ToString(); - } - if ((GroupName != null) && (GroupName != string.Empty)) - { - param["Name"] = GroupName.ToString(); - } - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroup", param); - - if (respData.Contains("error")) - { - return null; - } - - return GroupProfileHashtableToGroupRecord(respData); - - } - - public GroupProfileData GetMemberGroupProfile(GroupRequestID requestID, UUID GroupID, UUID AgentID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroup", param); - - if (respData.Contains("error")) - { - // GroupProfileData is not nullable - return new GroupProfileData(); - } - - GroupMembershipData MemberInfo = GetAgentGroupMembership(requestID, AgentID, GroupID); - GroupProfileData MemberGroupProfile = GroupProfileHashtableToGroupProfileData(respData); - - MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle; - MemberGroupProfile.PowersMask = MemberInfo.GroupPowers; - - return MemberGroupProfile; - - } - - private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile) - { - GroupProfileData group = new GroupProfileData(); - group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); - group.Name = (string)groupProfile["Name"]; - - if (groupProfile["Charter"] != null) - { - group.Charter = (string)groupProfile["Charter"]; - } - - group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; - group.InsigniaID = UUID.Parse((string)groupProfile["InsigniaID"]); - group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); - group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; - group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; - group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; - group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); - group.OwnerRole = UUID.Parse((string)groupProfile["OwnerRoleID"]); - - group.GroupMembershipCount = int.Parse((string)groupProfile["GroupMembershipCount"]); - group.GroupRolesCount = int.Parse((string)groupProfile["GroupRolesCount"]); - - return group; - } - - private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile) - { - - GroupRecord group = new GroupRecord(); - group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); - group.GroupName = groupProfile["Name"].ToString(); - if (groupProfile["Charter"] != null) - { - group.Charter = (string)groupProfile["Charter"]; - } - group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; - group.GroupPicture = UUID.Parse((string)groupProfile["InsigniaID"]); - group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); - group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; - group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; - group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; - group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); - group.OwnerRoleID = UUID.Parse((string)groupProfile["OwnerRoleID"]); - - return group; - } - - public void SetAgentActiveGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - - XmlRpcCall(requestID, "groups.setAgentActiveGroup", param); - } - - public void SetAgentActiveGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - param["SelectedRoleID"] = RoleID.ToString(); - - XmlRpcCall(requestID, "groups.setAgentGroupInfo", param); - } - - public void SetAgentGroupInfo(GroupRequestID requestID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - param["AcceptNotices"] = AcceptNotices ? "1" : "0"; - param["ListInProfile"] = ListInProfile ? "1" : "0"; - - XmlRpcCall(requestID, "groups.setAgentGroupInfo", param); - - } - - public void AddAgentToGroupInvite(GroupRequestID requestID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID) - { - Hashtable param = new Hashtable(); - param["InviteID"] = inviteID.ToString(); - param["AgentID"] = agentID.ToString(); - param["RoleID"] = roleID.ToString(); - param["GroupID"] = groupID.ToString(); - - XmlRpcCall(requestID, "groups.addAgentToGroupInvite", param); - - } - - public GroupInviteInfo GetAgentToGroupInvite(GroupRequestID requestID, UUID inviteID) - { - Hashtable param = new Hashtable(); - param["InviteID"] = inviteID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getAgentToGroupInvite", param); - - if (respData.Contains("error")) - { - return null; - } - - GroupInviteInfo inviteInfo = new GroupInviteInfo(); - inviteInfo.InviteID = inviteID; - inviteInfo.GroupID = UUID.Parse((string)respData["GroupID"]); - inviteInfo.RoleID = UUID.Parse((string)respData["RoleID"]); - inviteInfo.AgentID = UUID.Parse((string)respData["AgentID"]); - - return inviteInfo; - } - - public void RemoveAgentToGroupInvite(GroupRequestID requestID, UUID inviteID) - { - Hashtable param = new Hashtable(); - param["InviteID"] = inviteID.ToString(); - - XmlRpcCall(requestID, "groups.removeAgentToGroupInvite", param); - } - - public void AddAgentToGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - param["RoleID"] = RoleID.ToString(); - - XmlRpcCall(requestID, "groups.addAgentToGroup", param); - } - - public void RemoveAgentFromGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - - XmlRpcCall(requestID, "groups.removeAgentFromGroup", param); - } - - public void AddAgentToGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - param["RoleID"] = RoleID.ToString(); - - XmlRpcCall(requestID, "groups.addAgentToGroupRole", param); - } - - public void RemoveAgentFromGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - param["RoleID"] = RoleID.ToString(); - - XmlRpcCall(requestID, "groups.removeAgentFromGroupRole", param); - } - - - public List FindGroups(GroupRequestID requestID, string search) - { - Hashtable param = new Hashtable(); - param["Search"] = search; - - Hashtable respData = XmlRpcCall(requestID, "groups.findGroups", param); - - List findings = new List(); - - if (!respData.Contains("error")) - { - Hashtable results = (Hashtable)respData["results"]; - foreach (Hashtable groupFind in results.Values) - { - DirGroupsReplyData data = new DirGroupsReplyData(); - data.groupID = new UUID((string)groupFind["GroupID"]); ; - data.groupName = (string)groupFind["Name"]; - data.members = int.Parse((string)groupFind["Members"]); - // data.searchOrder = order; - - findings.Add(data); - } - } - - return findings; - } - - public GroupMembershipData GetAgentGroupMembership(GroupRequestID requestID, UUID AgentID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMembership", param); - - if (respData.Contains("error")) - { - return null; - } - - GroupMembershipData data = HashTableToGroupMembershipData(respData); - - return data; - } - - public GroupMembershipData GetAgentActiveMembership(GroupRequestID requestID, UUID AgentID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getAgentActiveMembership", param); - - if (respData.Contains("error")) - { - return null; - } - - return HashTableToGroupMembershipData(respData); - } - - - public List GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMemberships", param); - - List memberships = new List(); - - if (!respData.Contains("error")) - { - foreach (object membership in respData.Values) - { - memberships.Add(HashTableToGroupMembershipData((Hashtable)membership)); - } - } - - return memberships; - } - - public List GetAgentGroupRoles(GroupRequestID requestID, UUID AgentID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["AgentID"] = AgentID.ToString(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getAgentRoles", param); - - List Roles = new List(); - - if (respData.Contains("error")) - { - return Roles; - } - - foreach (Hashtable role in respData.Values) - { - GroupRolesData data = new GroupRolesData(); - data.RoleID = new UUID((string)role["RoleID"]); - data.Name = (string)role["Name"]; - data.Description = (string)role["Description"]; - data.Powers = ulong.Parse((string)role["Powers"]); - data.Title = (string)role["Title"]; - - Roles.Add(data); - } - - return Roles; - - - } - - public List GetGroupRoles(GroupRequestID requestID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoles", param); - - List Roles = new List(); - - if (respData.Contains("error")) - { - return Roles; - } - - foreach (Hashtable role in respData.Values) - { - GroupRolesData data = new GroupRolesData(); - data.Description = (string)role["Description"]; - data.Members = int.Parse((string)role["Members"]); - data.Name = (string)role["Name"]; - data.Powers = ulong.Parse((string)role["Powers"]); - data.RoleID = new UUID((string)role["RoleID"]); - data.Title = (string)role["Title"]; - - Roles.Add(data); - } - - return Roles; - - } - - private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData) - { - GroupMembershipData data = new GroupMembershipData(); - data.AcceptNotices = ((string)respData["AcceptNotices"] == "1"); - data.Contribution = int.Parse((string)respData["Contribution"]); - data.ListInProfile = ((string)respData["ListInProfile"] == "1"); - - data.ActiveRole = new UUID((string)respData["SelectedRoleID"]); - data.GroupTitle = (string)respData["Title"]; - - data.GroupPowers = ulong.Parse((string)respData["GroupPowers"]); - - // Is this group the agent's active group - - data.GroupID = new UUID((string)respData["GroupID"]); - - UUID ActiveGroup = new UUID((string)respData["ActiveGroupID"]); - data.Active = data.GroupID.Equals(ActiveGroup); - - data.AllowPublish = ((string)respData["AllowPublish"] == "1"); - if (respData["Charter"] != null) - { - data.Charter = (string)respData["Charter"]; - } - data.FounderID = new UUID((string)respData["FounderID"]); - data.GroupID = new UUID((string)respData["GroupID"]); - data.GroupName = (string)respData["GroupName"]; - data.GroupPicture = new UUID((string)respData["InsigniaID"]); - data.MaturePublish = ((string)respData["MaturePublish"] == "1"); - data.MembershipFee = int.Parse((string)respData["MembershipFee"]); - data.OpenEnrollment = ((string)respData["OpenEnrollment"] == "1"); - data.ShowInList = ((string)respData["ShowInList"] == "1"); - return data; - } - - public List GetGroupMembers(GroupRequestID requestID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroupMembers", param); - - List members = new List(); - - if (respData.Contains("error")) - { - return members; - } - - foreach (Hashtable membership in respData.Values) - { - GroupMembersData data = new GroupMembersData(); - - data.AcceptNotices = ((string)membership["AcceptNotices"]) == "1"; - data.AgentID = new UUID((string)membership["AgentID"]); - data.Contribution = int.Parse((string)membership["Contribution"]); - data.IsOwner = ((string)membership["IsOwner"]) == "1"; - data.ListInProfile = ((string)membership["ListInProfile"]) == "1"; - data.AgentPowers = ulong.Parse((string)membership["AgentPowers"]); - data.Title = (string)membership["Title"]; - - members.Add(data); - } - - return members; - - } - - public List GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoleMembers", param); - - List members = new List(); - - if (!respData.Contains("error")) - { - foreach (Hashtable membership in respData.Values) - { - GroupRoleMembersData data = new GroupRoleMembersData(); - - data.MemberID = new UUID((string)membership["AgentID"]); - data.RoleID = new UUID((string)membership["RoleID"]); - - members.Add(data); - } - } - return members; - } - - public List GetGroupNotices(GroupRequestID requestID, UUID GroupID) - { - Hashtable param = new Hashtable(); - param["GroupID"] = GroupID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotices", param); - - List values = new List(); - - if (!respData.Contains("error")) - { - foreach (Hashtable value in respData.Values) - { - GroupNoticeData data = new GroupNoticeData(); - data.NoticeID = UUID.Parse((string)value["NoticeID"]); - data.Timestamp = uint.Parse((string)value["Timestamp"]); - data.FromName = (string)value["FromName"]; - data.Subject = (string)value["Subject"]; - data.HasAttachment = false; - data.AssetType = 0; - - values.Add(data); - } - } - return values; - - } - public GroupNoticeInfo GetGroupNotice(GroupRequestID requestID, UUID noticeID) - { - Hashtable param = new Hashtable(); - param["NoticeID"] = noticeID.ToString(); - - Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotice", param); - - - if (respData.Contains("error")) - { - return null; - } - - GroupNoticeInfo data = new GroupNoticeInfo(); - data.GroupID = UUID.Parse((string)respData["GroupID"]); - data.Message = (string)respData["Message"]; - data.BinaryBucket = Utils.HexStringToBytes((string)respData["BinaryBucket"], true); - data.noticeData.NoticeID = UUID.Parse((string)respData["NoticeID"]); - data.noticeData.Timestamp = uint.Parse((string)respData["Timestamp"]); - data.noticeData.FromName = (string)respData["FromName"]; - data.noticeData.Subject = (string)respData["Subject"]; - data.noticeData.HasAttachment = false; - data.noticeData.AssetType = 0; - - if (data.Message == null) - { - data.Message = string.Empty; - } - - return data; - } - public void AddGroupNotice(GroupRequestID requestID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) - { - string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, ""); - - Hashtable param = new Hashtable(); - param["GroupID"] = groupID.ToString(); - param["NoticeID"] = noticeID.ToString(); - param["FromName"] = fromName; - param["Subject"] = subject; - param["Message"] = message; - param["BinaryBucket"] = binBucket; - param["TimeStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); - - XmlRpcCall(requestID, "groups.addGroupNotice", param); - } - - private Hashtable XmlRpcCall(GroupRequestID requestID, string function, Hashtable param) - { - if (requestID == null) - { - requestID = new GroupRequestID(); - } - param.Add("RequestingAgentID", requestID.AgentID.ToString()); - param.Add("RequestingAgentUserService", requestID.UserServiceURL); - param.Add("RequestingSessionID", requestID.SessionID.ToString()); - - - param.Add("ReadKey", m_groupReadKey); - param.Add("WriteKey", m_groupWriteKey); - - - IList parameters = new ArrayList(); - parameters.Add(param); - - XmlRpcRequest req; - if (!m_disableKeepAlive) - { - req = new XmlRpcRequest(function, parameters); - } - else - { - // This seems to solve a major problem on some windows servers - req = new NoKeepAliveXmlRpcRequest(function, parameters); - } - - XmlRpcResponse resp = null; - - try - { - resp = req.Send(m_serviceURL, 10000); - } - catch (Exception e) - { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", e.ToString()); - - - foreach (string key in param.Keys) - { - m_log.WarnFormat("[XMLRPCGROUPDATA]: {0} :: {1}", key, param[key].ToString()); - } - - Hashtable respData = new Hashtable(); - respData.Add("error", e.ToString()); - return respData; - } - - if (resp.Value is Hashtable) - { - Hashtable respData = (Hashtable)resp.Value; - if (respData.Contains("error") && !respData.Contains("succeed")) - { - LogRespDataToConsoleError(respData); - } - - return respData; - } - - m_log.ErrorFormat("[XMLRPCGROUPDATA]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); - - if (resp.Value is ArrayList) - { - ArrayList al = (ArrayList)resp.Value; - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Contains {0} elements", al.Count); - - foreach (object o in al) - { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} :: {1}", o.GetType().ToString(), o.ToString()); - } - } - else - { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Function returned: {0}", resp.Value.ToString()); - } - - Hashtable error = new Hashtable(); - error.Add("error", "invalid return value"); - return error; - } - - private void LogRespDataToConsoleError(Hashtable respData) - { - m_log.Error("[XMLRPCGROUPDATA]: Error:"); - - foreach (string key in respData.Keys) - { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Key: {0}", key); - - string[] lines = respData[key].ToString().Split(new char[] { '\n' }); - foreach (string line in lines) - { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0}", line); - } - - } - } - - } - - public class GroupNoticeInfo - { - public GroupNoticeData noticeData = new GroupNoticeData(); - public UUID GroupID = UUID.Zero; - public string Message = string.Empty; - public byte[] BinaryBucket = new byte[0]; - } -} - -namespace Nwc.XmlRpc -{ - using System; - using System.Collections; - using System.IO; - using System.Xml; - using System.Net; - using System.Text; - using System.Reflection; - - /// Class supporting the request side of an XML-RPC transaction. - public class NoKeepAliveXmlRpcRequest : XmlRpcRequest - { - private Encoding _encoding = new ASCIIEncoding(); - private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer(); - private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer(); - - /// Instantiate an XmlRpcRequest for a specified method and parameters. - /// String designating the object.method on the server the request - /// should be directed to. - /// ArrayList of XML-RPC type parameters to invoke the request with. - public NoKeepAliveXmlRpcRequest(String methodName, IList parameters) - { - MethodName = methodName; - _params = parameters; - } - - /// Send the request to the server. - /// String The url of the XML-RPC server. - /// XmlRpcResponse The response generated. - public XmlRpcResponse Send(String url) - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - if (request == null) - throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR, - XmlRpcErrorCodes.TRANSPORT_ERROR_MSG + ": Could not create request with " + url); - request.Method = "POST"; - request.ContentType = "text/xml"; - request.AllowWriteStreamBuffering = true; - request.KeepAlive = false; - - Stream stream = request.GetRequestStream(); - XmlTextWriter xml = new XmlTextWriter(stream, _encoding); - _serializer.Serialize(xml, this); - xml.Flush(); - xml.Close(); - - HttpWebResponse response = (HttpWebResponse)request.GetResponse(); - StreamReader input = new StreamReader(response.GetResponseStream()); - - XmlRpcResponse resp = (XmlRpcResponse)_deserializer.Deserialize(input); - input.Close(); - response.Close(); - return resp; - } - } -} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs deleted file mode 100644 index 4095041..0000000 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Reflection; - - -using log4net; -using Nini.Config; - -using OpenMetaverse; -using 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.Capabilities.Caps; - -namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups -{ - public class XmlRpcGroupsMessaging : ISharedRegionModule - { - - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private List m_sceneList = new List(); - - private IMessageTransferModule m_msgTransferModule = null; - - private IGroupsModule m_groupsModule = null; - - // TODO: Move this off to the xmlrpc server - public Dictionary> m_agentsInGroupSession = new Dictionary>(); - public Dictionary> m_agentsDroppedSession = new Dictionary>(); - - - // Config Options - private bool m_groupMessagingEnabled = false; - private bool m_debugEnabled = true; - - #region IRegionModuleBase Members - - public void Initialise(IConfigSource config) - { - IConfig groupsConfig = config.Configs["Groups"]; - - if (groupsConfig == null) - { - // Do not run this module by default. - return; - } - else - { - if (!groupsConfig.GetBoolean("Enabled", false)) - { - return; - } - - if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") - { - m_groupMessagingEnabled = false; - - return; - } - - m_groupMessagingEnabled = groupsConfig.GetBoolean("XmlRpcMessagingEnabled", true); - - if (!m_groupMessagingEnabled) - { - return; - } - - m_log.Info("[GROUPS-MESSAGING]: Initializing XmlRpcGroupsMessaging"); - - m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); - } - - m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroupsMessaging starting up"); - - } - - public void AddRegion(Scene scene) - { - // NoOp - } - public void RegionLoaded(Scene scene) - { - if (!m_groupMessagingEnabled) - return; - - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_groupsModule = scene.RequestModuleInterface(); - - // No groups module, no groups messaging - if (m_groupsModule == null) - { - m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsModule, XmlRpcGroupsMessaging is now disabled."); - Close(); - m_groupMessagingEnabled = false; - return; - } - - m_msgTransferModule = scene.RequestModuleInterface(); - - // No message transfer module, no groups messaging - if (m_msgTransferModule == null) - { - m_log.Error("[GROUPS-MESSAGING]: Could not get MessageTransferModule"); - Close(); - m_groupMessagingEnabled = false; - return; - } - - - m_sceneList.Add(scene); - - scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - - } - - public void RemoveRegion(Scene scene) - { - if (!m_groupMessagingEnabled) - return; - - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_sceneList.Remove(scene); - } - - public void Close() - { - if (!m_groupMessagingEnabled) - return; - - if (m_debugEnabled) m_log.Debug("[GROUPS-MESSAGING]: Shutting down XmlRpcGroupsMessaging module."); - - foreach (Scene scene in m_sceneList) - { - scene.EventManager.OnNewClient -= OnNewClient; - scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; - } - - m_sceneList.Clear(); - - m_groupsModule = null; - m_msgTransferModule = null; - } - - public Type ReplacableInterface - { - get { return null; } - } - - public string Name - { - get { return "XmlRpcGroupsMessaging"; } - } - - #endregion - - #region ISharedRegionModule Members - - public void PostInitialise() - { - // NoOp - } - - #endregion - - #region SimGridEventHandlers - - private void OnNewClient(IClientAPI client) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); - - client.OnInstantMessage += OnInstantMessage; - } - - private void OnGridInstantMessage(GridInstantMessage msg) - { - // The instant message module will only deliver messages of dialog types: - // MessageFromAgent, StartTyping, StopTyping, MessageFromObject - // - // Any other message type will not be delivered to a client by the - // Instant Message Module - - - if (m_debugEnabled) - { - m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - DebugGridInstantMessage(msg); - } - - // Incoming message from a group - if ((msg.fromGroup == true) && - ((msg.dialog == (byte)InstantMessageDialog.SessionSend) - || (msg.dialog == (byte)InstantMessageDialog.SessionAdd) - || (msg.dialog == (byte)InstantMessageDialog.SessionDrop))) - { - ProcessMessageFromGroupSession(msg); - } - } - - private void ProcessMessageFromGroupSession(GridInstantMessage msg) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); - - switch (msg.dialog) - { - case (byte)InstantMessageDialog.SessionAdd: - AddAgentToGroupSession(msg.fromAgentID, msg.imSessionID); - break; - - case (byte)InstantMessageDialog.SessionDrop: - RemoveAgentFromGroupSession(msg.fromAgentID, msg.imSessionID); - break; - - case (byte)InstantMessageDialog.SessionSend: - if (!m_agentsInGroupSession.ContainsKey(msg.toAgentID) - && !m_agentsDroppedSession.ContainsKey(msg.toAgentID)) - { - // Agent not in session and hasn't dropped from session - // Add them to the session for now, and Invite them - AddAgentToGroupSession(msg.toAgentID, msg.imSessionID); - - UUID toAgentID = new UUID(msg.toAgentID); - IClientAPI activeClient = GetActiveClient(toAgentID); - if (activeClient != null) - { - UUID groupID = new UUID(msg.fromAgentID); - - GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); - if (groupInfo != null) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message"); - - // Force? open the group session dialog??? - IEventQueue eq = activeClient.Scene.RequestModuleInterface(); - eq.ChatterboxInvitation( - groupID - , groupInfo.GroupName - , new UUID(msg.fromAgentID) - , msg.message, new UUID(msg.toAgentID) - , msg.fromAgentName - , msg.dialog - , msg.timestamp - , msg.offline == 1 - , (int)msg.ParentEstateID - , msg.Position - , 1 - , new UUID(msg.imSessionID) - , msg.fromGroup - , Utils.StringToBytes(groupInfo.GroupName) - ); - - eq.ChatterBoxSessionAgentListUpdates( - new UUID(groupID) - , new UUID(msg.fromAgentID) - , new UUID(msg.toAgentID) - , false //canVoiceChat - , false //isModerator - , false //text mute - ); - } - } - } - else if (!m_agentsDroppedSession.ContainsKey(msg.toAgentID)) - { - // User hasn't dropped, so they're in the session, - // maybe we should deliver it. - IClientAPI client = GetActiveClient(new UUID(msg.toAgentID)); - if (client != null) - { - // Deliver locally, directly - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name); - client.SendInstantMessage(msg); - } - else - { - m_log.WarnFormat("[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID); - } - } - break; - - default: - m_log.WarnFormat("[GROUPS-MESSAGING]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString()); - break; - } - } - - #endregion - - #region ClientEvents - - private void RemoveAgentFromGroupSession(Guid agentID, Guid sessionID) - { - if (m_agentsInGroupSession.ContainsKey(sessionID)) - { - // If in session remove - if (m_agentsInGroupSession[sessionID].Contains(agentID)) - { - m_agentsInGroupSession[sessionID].Remove(agentID); - } - - // If not in dropped list, add - if (!m_agentsDroppedSession[sessionID].Contains(agentID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Dropped {1} from session {0}", sessionID, agentID); - m_agentsDroppedSession[sessionID].Add(agentID); - } - } - } - - private void AddAgentToGroupSession(Guid agentID, Guid sessionID) - { - // Add Session Status if it doesn't exist for this session - CreateGroupSessionTracking(sessionID); - - // If nessesary, remove from dropped list - if (m_agentsDroppedSession[sessionID].Contains(agentID)) - { - m_agentsDroppedSession[sessionID].Remove(agentID); - } - - // If nessesary, add to in session list - if (!m_agentsInGroupSession[sessionID].Contains(agentID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Added {1} to session {0}", sessionID, agentID); - m_agentsInGroupSession[sessionID].Add(agentID); - } - } - - private void CreateGroupSessionTracking(Guid sessionID) - { - if (!m_agentsInGroupSession.ContainsKey(sessionID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Creating session tracking for : {0}", sessionID); - m_agentsInGroupSession.Add(sessionID, new List()); - m_agentsDroppedSession.Add(sessionID, new List()); - } - } - - private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) - { - if (m_debugEnabled) - { - m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - DebugGridInstantMessage(im); - } - - // Start group IM session - if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) - { - UUID groupID = new UUID(im.toAgentID); - - GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); - if (groupInfo != null) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Start Group Session for {0}", groupInfo.GroupName); - - AddAgentToGroupSession(im.fromAgentID, im.imSessionID); - - ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, groupID); - - IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); - queue.ChatterBoxSessionAgentListUpdates( - new UUID(groupID) - , new UUID(im.fromAgentID) - , new UUID(im.toAgentID) - , false //canVoiceChat - , false //isModerator - , false //text mute - ); - } - } - - // Send a message from locally connected client to a group - if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) - { - UUID groupID = new UUID(im.toAgentID); - - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", groupID, im.imSessionID.ToString()); - - SendMessageToGroup(im, groupID); - } - } - - #endregion - - private void SendMessageToGroup(GridInstantMessage im, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - foreach (GroupMembersData member in m_groupsModule.GroupMembersRequest(null, groupID)) - { - if (m_agentsDroppedSession[im.imSessionID].Contains(member.AgentID.Guid)) - { - // Don't deliver messages to people who have dropped this session - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); - continue; - } - - // Copy Message - GridInstantMessage msg = new GridInstantMessage(); - msg.imSessionID = im.imSessionID; - msg.fromAgentName = im.fromAgentName; - msg.message = im.message; - msg.dialog = im.dialog; - msg.offline = im.offline; - msg.ParentEstateID = im.ParentEstateID; - msg.Position = im.Position; - msg.RegionID = im.RegionID; - msg.binaryBucket = im.binaryBucket; - msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); - - // Updat Pertinate fields to make it a "group message" - msg.fromAgentID = groupID.Guid; - msg.fromGroup = true; - - msg.toAgentID = member.AgentID.Guid; - - IClientAPI client = GetActiveClient(member.AgentID); - if (client == null) - { - // If they're not local, forward across the grid - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); - m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); - } - else - { - // Deliver locally, directly - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); - ProcessMessageFromGroupSession(msg); - } - } - } - - void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - OSDMap moderatedMap = new OSDMap(4); - moderatedMap.Add("voice", OSD.FromBoolean(false)); - - OSDMap sessionMap = new OSDMap(4); - sessionMap.Add("moderated_mode", moderatedMap); - sessionMap.Add("session_name", OSD.FromString(groupName)); - sessionMap.Add("type", OSD.FromInteger(0)); - sessionMap.Add("voice_enabled", OSD.FromBoolean(false)); - - OSDMap bodyMap = new OSDMap(4); - bodyMap.Add("session_id", OSD.FromUUID(groupID)); - bodyMap.Add("temp_session_id", OSD.FromUUID(groupID)); - bodyMap.Add("success", OSD.FromBoolean(true)); - bodyMap.Add("session_info", sessionMap); - - IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); - - if (queue != null) - { - queue.Enqueue(EventQueueHelper.buildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); - } - } - - private void DebugGridInstantMessage(GridInstantMessage im) - { - // Don't log any normal IMs (privacy!) - if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent) - { - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False"); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID.ToString()); - m_log.WarnFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); - } - } - - #region Client Tools - - /// - /// Try to find an active IClientAPI reference for agentID giving preference to root connections - /// - private IClientAPI GetActiveClient(UUID agentID) - { - IClientAPI child = null; - - // Try root avatar first - foreach (Scene scene in m_sceneList) - { - if (scene.Entities.ContainsKey(agentID) && - scene.Entities[agentID] is ScenePresence) - { - ScenePresence user = (ScenePresence)scene.Entities[agentID]; - if (!user.IsChildAgent) - { - return user.ControllingClient; - } - else - { - child = user.ControllingClient; - } - } - } - - // If we didn't find a root, then just return whichever child we found, or null if none - return child; - } - - #endregion - } -} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs deleted file mode 100644 index 2cbc571..0000000 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs +++ /dev/null @@ -1,1331 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Timers; - -using log4net; -using Nini.Config; - -using OpenMetaverse; -using OpenMetaverse.StructuredData; - -using OpenSim.Framework; -using OpenSim.Framework.Communications; -using OpenSim.Region.CoreModules.Framework.EventQueue; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; - -using Caps = OpenSim.Framework.Capabilities.Caps; -using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; - - - -namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups -{ - public class XmlRpcGroupsModule : ISharedRegionModule, 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 - /// XmlRpcServiceReadKey = 1234 - /// XmlRpcServiceWriteKey = 1234 - /// - /// ; 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(); - - private IMessageTransferModule m_msgTransferModule = null; - - private IGroupDataProvider m_groupData = null; - - class GroupRequestIDInfo - { - public GroupRequestID RequestID = new GroupRequestID(); - public DateTime LastUsedTMStamp = DateTime.MinValue; - } - private Dictionary m_clientRequestIDInfo = new Dictionary(); - private const int m_clientRequestIDFlushTimeOut = 300000; // Every 5 minutes - private Timer m_clientRequestIDFlushTimer = new Timer(); - - - // 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 IRegionModuleBase Members - - public void Initialise(IConfigSource config) - { - IConfig groupsConfig = config.Configs["Groups"]; - - if (groupsConfig == null) - { - // Do not run this module by default. - return; - } - else - { - m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); - if (!m_groupsEnabled) - { - return; - } - - if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") - { - m_groupsEnabled = false; - - return; - } - - m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name); - - string ServiceURL = groupsConfig.GetString("XmlRpcServiceURL", m_defaultXmlRpcServiceURL); - bool DisableKeepAlive = groupsConfig.GetBoolean("XmlRpcDisableKeepAlive", false); - - string ServiceReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty); - string ServiceWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty); - - m_groupData = new XmlRpcGroupDataProvider(ServiceURL, DisableKeepAlive, ServiceReadKey, ServiceWriteKey); - m_log.InfoFormat("[GROUPS]: XmlRpc Service URL set to: {0}", ServiceURL); - - m_groupNoticesEnabled = groupsConfig.GetBoolean("XmlRpcNoticesEnabled", true); - m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); - - m_clientRequestIDFlushTimer.Interval = m_clientRequestIDFlushTimeOut; - m_clientRequestIDFlushTimer.Elapsed += FlushClientRequestIDInfoCache; - m_clientRequestIDFlushTimer.AutoReset = true; - m_clientRequestIDFlushTimer.Start(); - } - } - - void FlushClientRequestIDInfoCache(object sender, ElapsedEventArgs e) - { - lock (m_clientRequestIDInfo) - { - TimeSpan cacheTimeout = new TimeSpan(0,0, m_clientRequestIDFlushTimeOut / 1000); - UUID[] CurrentKeys = new UUID[m_clientRequestIDInfo.Count]; - foreach (UUID key in CurrentKeys) - { - if (DateTime.Now - m_clientRequestIDInfo[key].LastUsedTMStamp > cacheTimeout) - { - m_clientRequestIDInfo.Remove(key); - } - } - } - } - - 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - if (m_msgTransferModule == null) - { - m_msgTransferModule = scene.RequestModuleInterface(); - - // No message transfer module, no notices, group invites, rejects, ejects, etc - if (m_msgTransferModule == null) - { - m_groupsEnabled = false; - m_log.Error("[GROUPS]: Could not get MessageTransferModule"); - Close(); - return; - } - } - - lock (m_sceneList) - { - m_sceneList.Add(scene); - } - - scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - - // 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) - { - if (!m_groupsEnabled) - return; - - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - lock (m_sceneList) - { - m_sceneList.Remove(scene); - } - } - - public void Close() - { - if (!m_groupsEnabled) - return; - - if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module."); - - m_clientRequestIDFlushTimer.Stop(); - } - - public Type ReplacableInterface - { - get { return null; } - } - - public string Name - { - get { return "XmlRpcGroupsModule"; } - } - - #endregion - - #region ISharedRegionModule Members - - public void PostInitialise() - { - // NoOp - } - - #endregion - - #region EventHandlers - private void OnNewClient(IClientAPI client) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; - client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; - client.OnDirFindQuery += OnDirFindQuery; - client.OnRequestAvatarProperties += OnRequestAvatarProperties; - - // Used for Notices and Group Invites/Accept/Reject - client.OnInstantMessage += OnInstantMessage; - - lock (m_clientRequestIDInfo) - { - if (m_clientRequestIDInfo.ContainsKey(client.AgentId)) - { - // flush any old RequestID information - m_clientRequestIDInfo.Remove(client.AgentId); - } - } - SendAgentGroupDataUpdate(client, client.AgentId); - } - - private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) - { - GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), avatarID).ToArray(); - remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups); - } - - /* - * This becomes very problematic in a shared module. In a shared module you may have more then one - * 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, - * 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) - { - if (m_debugEnabled) m_log.DebugFormat("[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 - { - if (m_debugEnabled) m_log.WarnFormat("[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) - { - if (m_debugEnabled) m_log.DebugFormat("[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(GetClientGroupRequestID(remoteClient), queryText).ToArray()); - } - - } - - private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - UUID activeGroupID = UUID.Zero; - string activeGroupTitle = string.Empty; - string activeGroupName = string.Empty; - ulong activeGroupPowers = (ulong)GroupPowers.None; - - GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetClientGroupRequestID(remoteClient), dataForAgentID); - if (membership != null) - { - activeGroupID = membership.GroupID; - activeGroupTitle = membership.GroupTitle; - activeGroupPowers = membership.GroupPowers; - } - - SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle); - - SendScenePresenceUpdate(dataForAgentID, activeGroupTitle); - } - - private void HandleUUIDGroupNameRequest(UUID GroupID,IClientAPI remoteClient) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - string GroupName; - - GroupRecord group = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // Group invitations - if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) - { - UUID inviteID = new UUID(im.imSessionID); - GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); - - if (inviteInfo == null) - { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: 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); - - UUID fromAgentID = new UUID(im.fromAgentID); - if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) - { - // Accept - if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice."); - - // and the sessionid is the role - m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); - - 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]; - - OutgoingInstantMessage(msg, inviteInfo.AgentID); - - UpdateAllClientsWithGroupInfo(inviteInfo.AgentID); - - // TODO: If the inviter is still online, they need an agent dataupdate - // and maybe group membership updates for the invitee - - m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); - } - - // Reject - if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice."); - m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID); - } - } - } - - // Group notices - if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) - { - if (!m_groupNoticesEnabled) - { - return; - } - - UUID GroupID = new UUID(im.toAgentID); - if (m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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(); - 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) - { - 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(GetClientGroupRequestID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); - if (OnNewGroupNotice != null) - { - OnNewGroupNotice(GroupID, NoticeID); - } - - // Send notice out to everyone that wants notices - foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), GroupID)) - { - if (member.AcceptNotices) - { - // Build notice IIM - GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); - - msg.toAgentID = member.AgentID.Guid; - OutgoingInstantMessage(msg, member.AgentID); - } - } - } - } - - // 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. - - UUID ejecteeID = new UUID(im.toAgentID); - - im.dialog = (byte)InstantMessageDialog.MessageFromAgent; - OutgoingInstantMessage(im, ejecteeID); - - IClientAPI ejectee = GetActiveClient(ejecteeID); - if (ejectee != null) - { - UUID groupID = new UUID(im.fromAgentID); - ejectee.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); - IClientAPI localClient = GetActiveClient(toAgentID); - if (localClient != null) - { - localClient.SendInstantMessage(msg); - } - break; - } - } - } - - #endregion - - #region IGroupsModule Members - - public event NewGroupNotice OnNewGroupNotice; - - public GroupRecord GetGroupRecord(UUID GroupID) - { - return m_groupData.GetGroupRecord(null, GroupID, null); - } - - public void ActivateGroup(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_groupData.SetAgentActiveGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, 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 - // updated with new group info. At a minimum, they should get ScenePresence - // updated with new title. - UpdateAllClientsWithGroupInfo(remoteClient.AgentId); - } - - /// - /// Get the Role Titles for an Agent, for a specific group - /// - public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - List agentRoles = m_groupData.GetAgentGroupRoles(grID, remoteClient.AgentId, groupID); - GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(grID, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - List data = m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), groupID); - if (m_debugEnabled) - { - foreach (GroupMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: {0} {1}", member.AgentID, member.Title); - } - } - - return data; - - } - - public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - List data = m_groupData.GetGroupRoles(GetClientGroupRequestID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupRolesData member in data) - { - m_log.DebugFormat("[GROUPS]: {0} {1}", member.Title, member.Members); - } - } - - return data; - - } - - public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - List data = m_groupData.GetGroupRoleMembers(GetClientGroupRequestID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupRoleMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: Av: {0} Role: {1}", member.MemberID, member.RoleID); - } - } - - return data; - - - } - - public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupProfileData profile = new GroupProfileData(); - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - GroupRecord groupInfo = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), 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(grID, groupID).Count; - profile.GroupRolesCount = m_groupData.GetGroupRoles(grID, 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(grID, remoteClient.AgentId, groupID); - if (memberInfo != null) - { - profile.MemberTitle = memberInfo.GroupTitle; - profile.PowersMask = memberInfo.GroupPowers; - } - - return profile; - } - - public GroupMembershipData[] GetMembershipData(UUID agentID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - return m_groupData.GetAgentGroupMemberships(null, agentID).ToArray(); - } - - public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - return m_groupData.GetAgentGroupMembership(null, agentID, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // TODO: Security Check? - - m_groupData.UpdateGroup(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_groupData.SetAgentGroupInfo(GetClientGroupRequestID(remoteClient), 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - if (m_groupData.GetGroupRecord(grID, UUID.Zero, name) != null) - { - remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); - return UUID.Zero; - } - // is there is a money module present ? - IMoneyModule money=remoteClient.Scene.RequestModuleInterface(); - if (money != null) - { - // do the transaction, that is if the agent has got sufficient funds - if (!money.GroupCreationCovered(remoteClient)) { - remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got issuficient funds to create a group."); - return UUID.Zero; - } - money.ApplyGroupCreationCharge(remoteClient.AgentId); - } - UUID groupID = m_groupData.CreateGroup(grID, name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId); - - remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); - - // Update the founder with new group information. - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - - return groupID; - } - - public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[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(GetClientGroupRequestID(remoteClient), groupID).ToArray(); - } - - /// - /// Get the title of the agent's current role. - /// - public string GetGroupTitle(UUID avatarID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupMembershipData membership = m_groupData.GetAgentActiveMembership(null, 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.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_groupData.SetAgentActiveGroupRole(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, titleRoleID); - - // 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(remoteClient.AgentId).GroupID == GroupID) - - UpdateAllClientsWithGroupInfo(remoteClient.AgentId); - } - - - public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // TODO: Security Checks? - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - switch ((OpenMetaverse.GroupRoleUpdate)updateType) - { - case OpenMetaverse.GroupRoleUpdate.Create: - m_groupData.AddGroupRole(grID, groupID, UUID.Random(), name, description, title, powers); - break; - - case OpenMetaverse.GroupRoleUpdate.Delete: - m_groupData.RemoveGroupRole(grID, groupID, roleID); - break; - - case OpenMetaverse.GroupRoleUpdate.UpdateAll: - case OpenMetaverse.GroupRoleUpdate.UpdateData: - case OpenMetaverse.GroupRoleUpdate.UpdatePowers: - m_groupData.UpdateGroupRole(grID, groupID, roleID, name, description, title, powers); - break; - - case OpenMetaverse.GroupRoleUpdate.NoUpdate: - default: - // No Op - break; - - } - - // TODO: This update really should send out updates for everyone in the role that just got changed. - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - } - - public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - // Todo: Security check - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - switch (changes) - { - case 0: - // Add - m_groupData.AddAgentToGroupRole(grID, memberID, groupID, roleID); - - break; - case 1: - // Remove - m_groupData.RemoveAgentFromGroupRole(grID, memberID, groupID, roleID); - - break; - default: - m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); - break; - } - - // TODO: This update really should send out updates for everyone in the role that just got changed. - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - } - - public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - GroupNoticeInfo data = m_groupData.GetGroupNotice(grID, groupNoticeID); - - if (data != null) - { - GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, data.GroupID, 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 : " + 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; - - OutgoingInstantMessage(msg, remoteClient.AgentId); - } - - } - - public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", 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)1; // Allow this message to be stored for offline use - msg.ParentEstateID = 0; - msg.Position = Vector3.Zero; - msg.RegionID = UUID.Zero.Guid; - - GroupNoticeInfo info = m_groupData.GetGroupNotice(null, 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; - } - else - { - 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.fromAgentName = string.Empty; - msg.message = string.Empty; - msg.binaryBucket = new byte[0]; - } - - return msg; - } - - public void SendAgentGroupDataUpdate(IClientAPI remoteClient) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // Send agent information about his groups - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - } - - public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // Should check to see if OpenEnrollment, or if there's an outstanding invitation - m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, UUID.Zero); - - remoteClient.SendJoinGroupReply(groupID, true); - - // Should this send updates to everyone in the group? - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - } - - public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - m_groupData.RemoveAgentFromGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID); - - remoteClient.SendLeaveGroupReply(groupID, true); - - remoteClient.SendAgentDropGroup(groupID); - - // SL sends out notifcations to the group messaging session that the person has left - // Should this also update everyone who is in the group? - SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId); - } - - public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupRequestID grID = GetClientGroupRequestID(remoteClient); - - // Todo: Security check? - m_groupData.RemoveAgentFromGroup(grID, ejecteeID, groupID); - - remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true); - - GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, 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]; - OutgoingInstantMessage(msg, ejecteeID); - - - // 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]; - OutgoingInstantMessage(msg, remoteClient.AgentId); - - - // SL sends out messages to everyone in the group - // Who all should receive updates and what should they be updated with? - UpdateAllClientsWithGroupInfo(ejecteeID); - } - - public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // Todo: Security check, probably also want to send some kind of notification - UUID InviteID = UUID.Random(); - GroupRequestID grid = GetClientGroupRequestID(remoteClient); - - m_groupData.AddAgentToGroupInvite(grid, InviteID, groupID, roleID, invitedAgentID); - - // Check to see if the invite went through, if it did not then it's possible - // the remoteClient did not validate or did not have permission to invite. - GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(grid, InviteID); - - if (inviteInfo != null) - { - 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]; - - OutgoingInstantMessage(msg, invitedAgentID); - } - } - } - - #endregion - - #region Client/Update Tools - - /// - /// Try to find an active IClientAPI reference for agentID giving preference to root connections - /// - private IClientAPI GetActiveClient(UUID agentID) - { - IClientAPI child = null; - - // Try root avatar first - foreach (Scene scene in m_sceneList) - { - if (scene.Entities.ContainsKey(agentID) && - scene.Entities[agentID] is ScenePresence) - { - ScenePresence user = (ScenePresence)scene.Entities[agentID]; - if (!user.IsChildAgent) - { - return user.ControllingClient; - } - else - { - child = user.ControllingClient; - } - } - } - - // If we didn't find a root, then just return whichever child we found, or null if none - return child; - } - - private GroupRequestID GetClientGroupRequestID(IClientAPI client) - { - if (client == null) - { - return new GroupRequestID(); - } - - lock (m_clientRequestIDInfo) - { - if (!m_clientRequestIDInfo.ContainsKey(client.AgentId)) - { - GroupRequestIDInfo info = new GroupRequestIDInfo(); - info.RequestID.AgentID = client.AgentId; - info.RequestID.SessionID = client.SessionId; - - UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(client.AgentId); - if (userProfile == null) - { - // This should be impossible. If I've been passed a reference to a client - // that client should be registered with the UserService. So something - // is horribly wrong somewhere. - - m_log.WarnFormat("[GROUPS]: Could not find a user profile for {0} / {1}", client.Name, client.AgentId); - - // Default to local user service and hope for the best? - info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL; - - } - else if (userProfile is ForeignUserProfileData) - { - // They aren't from around here - ForeignUserProfileData fupd = (ForeignUserProfileData)userProfile; - info.RequestID.UserServiceURL = fupd.UserServerURI; - } - else - { - // They're a local user, use this: - info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL; - } - - m_clientRequestIDInfo.Add(client.AgentId, info); - } - - m_clientRequestIDInfo[client.AgentId].LastUsedTMStamp = DateTime.Now; - } - return m_clientRequestIDInfo[client.AgentId].RequestID; - } - - /// - /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'. - /// - private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, 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(dataForAgentID)); - 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); - } - - } - - private void SendScenePresenceUpdate(UUID AgentID, string Title) - { - if (m_debugEnabled) 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(); - } - } - } - } - - /// - /// Send updates to all clients who might be interested in groups data for dataForClientID - /// - private void UpdateAllClientsWithGroupInfo(UUID dataForClientID) - { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - // TODO: Probably isn't nessesary to update every client in every scene. - // Need to examine client updates and do only what's nessesary. - lock (m_sceneList) - { - foreach (Scene scene in m_sceneList) - { - scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); }); - } - } - } - - /// - /// Update remoteClient with group information about dataForAgentID - /// - private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID) - { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name); - - // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff - - OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero); - - - // Need to send a group membership update to the client - // UDP version doesn't seem to behave nicely. But we're going to send it out here - // with an empty group membership to hopefully remove groups being displayed due - // to the core Groups Stub - remoteClient.SendGroupMembership(new GroupMembershipData[0]); - - GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), dataForAgentID).ToArray(); - - SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipData); - remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipData); - - } - - 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); - - // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff - UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(dataForAgentID); - string firstname, lastname; - if (userProfile != null) - { - firstname = userProfile.FirstName; - lastname = userProfile.SurName; - } - else - { - firstname = "Unknown"; - lastname = "Unknown"; - } - - remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, - lastname, activeGroupPowers, activeGroupName, - activeGroupTitle); - } - - #endregion - - #region IM Backed Processes - - private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo) - { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - IClientAPI localClient = GetActiveClient(msgTo); - if (localClient != null) - { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is local, delivering directly", localClient.Name); - localClient.SendInstantMessage(msg); - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo); - m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Message Sent: {0}", success?"Succeeded":"Failed"); }); - } - } - - public void NotifyChange(UUID groupID) - { - // Notify all group members of a chnge in group roles and/or - // permissions - // - } - - #endregion - } - -} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs new file mode 100644 index 0000000..03fe109 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs @@ -0,0 +1,1007 @@ +/* + * 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; +using System.Collections.Generic; +using System.Reflection; + +using Nwc.XmlRpc; + +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + public class XmlRpcGroupsServicesConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + + public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | + GroupPowers.Accountable | + GroupPowers.JoinChat | + GroupPowers.AllowVoiceChat | + GroupPowers.ReceiveNotices | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + private bool m_connectorEnabled = false; + + private string m_serviceURL = string.Empty; + + private bool m_disableKeepAlive = false; + + private string m_groupReadKey = string.Empty; + private string m_groupWriteKey = string.Empty; + + + #region IRegionModuleBase Members + + public string Name + { + get { return "XmlRpcGroupsServicesConnector"; } + } + + // this module is not intended to be replaced, but there should only be 1 of them. + public Type ReplacableInterface + { + get { return null; } + } + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + { + // Do not run this module by default. + return; + } + else + { + // if groups aren't enabled, we're not needed. + // if we're not specified as the connector to use, then we're not wanted + if ( (groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("GroupsServicesConnectorModule", "Default") != Name)) + { + m_connectorEnabled = false; + return; + } + + m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); + + m_serviceURL = groupsConfig.GetString("XmlRpcServiceURL", string.Empty); + if ((m_serviceURL == null) || + (m_serviceURL == string.Empty)) + { + m_log.ErrorFormat("Please specify a valid URL for XmlRpcServiceURL in OpenSim.ini, [Groups]"); + m_connectorEnabled = false; + return; + } + + m_disableKeepAlive = groupsConfig.GetBoolean("XmlRpcDisableKeepAlive", false); + + m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty); + m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty); + + // If we got all the config options we need, lets start'er'up + m_connectorEnabled = true; + } + } + + public void Close() + { + m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); + } + + public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) + { + if (m_connectorEnabled) + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(OpenSim.Region.Framework.Scenes.Scene scene) + { + if (scene.RequestModuleInterface() == this) + scene.UnregisterModuleInterface(this); + } + + public void RegionLoaded(OpenSim.Region.Framework.Scenes.Scene scene) + { + // TODO: May want to consider listenning for Agent Connections so we can pre-cache group info + // scene.EventManager.OnNewClient += OnNewClient; + } + + #endregion + + #region ISharedRegionModule Members + + public void PostInitialise() + { + // NoOp + } + + #endregion + + + + #region IGroupsServicesConnector Members + + /// + /// Create a Group, including Everyone and Owners Role, place FounderID in both groups, select Owner as selected role, and newly created group as agent's active role. + /// + public UUID CreateGroup(GroupRequestID requestID, string name, string charter, bool showInList, UUID insigniaID, + int membershipFee, bool openEnrollment, bool allowPublish, + bool maturePublish, UUID founderID) + { + UUID GroupID = UUID.Random(); + UUID OwnerRoleID = UUID.Random(); + + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + param["Name"] = name; + param["Charter"] = charter; + param["ShowInList"] = showInList == true ? 1 : 0; + param["InsigniaID"] = insigniaID.ToString(); + param["MembershipFee"] = 0; + param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; + param["AllowPublish"] = allowPublish == true ? 1 : 0; + param["MaturePublish"] = maturePublish == true ? 1 : 0; + param["FounderID"] = founderID.ToString(); + param["EveryonePowers"] = ((ulong)m_DefaultEveryonePowers).ToString(); + param["OwnerRoleID"] = OwnerRoleID.ToString(); + + // Would this be cleaner as (GroupPowers)ulong.MaxValue; + GroupPowers OwnerPowers = GroupPowers.Accountable + | GroupPowers.AllowEditLand + | GroupPowers.AllowFly + | GroupPowers.AllowLandmark + | GroupPowers.AllowRez + | GroupPowers.AllowSetHome + | GroupPowers.AllowVoiceChat + | GroupPowers.AssignMember + | GroupPowers.AssignMemberLimited + | GroupPowers.ChangeActions + | GroupPowers.ChangeIdentity + | GroupPowers.ChangeMedia + | GroupPowers.ChangeOptions + | GroupPowers.CreateRole + | GroupPowers.DeedObject + | GroupPowers.DeleteRole + | GroupPowers.Eject + | GroupPowers.FindPlaces + | GroupPowers.Invite + | GroupPowers.JoinChat + | GroupPowers.LandChangeIdentity + | GroupPowers.LandDeed + | GroupPowers.LandDivideJoin + | GroupPowers.LandEdit + | GroupPowers.LandEjectAndFreeze + | GroupPowers.LandGardening + | GroupPowers.LandManageAllowed + | GroupPowers.LandManageBanned + | GroupPowers.LandManagePasses + | GroupPowers.LandOptions + | GroupPowers.LandRelease + | GroupPowers.LandSetSale + | GroupPowers.ModerateChat + | GroupPowers.ObjectManipulate + | GroupPowers.ObjectSetForSale + | GroupPowers.ReceiveNotices + | GroupPowers.RemoveMember + | GroupPowers.ReturnGroupOwned + | GroupPowers.ReturnGroupSet + | GroupPowers.ReturnNonGroup + | GroupPowers.RoleProperties + | GroupPowers.SendNotices + | GroupPowers.SetLandingPoint + | GroupPowers.StartProposal + | GroupPowers.VoteOnProposal; + param["OwnersPowers"] = ((ulong)OwnerPowers).ToString(); + + + + + Hashtable respData = XmlRpcCall(requestID, "groups.createGroup", param); + + if (respData.Contains("error")) + { + // UUID is not nullable + + return UUID.Zero; + } + + return UUID.Parse((string)respData["GroupID"]); + } + + public void UpdateGroup(GroupRequestID requestID, UUID groupID, string charter, bool showInList, + UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["Charter"] = charter; + param["ShowInList"] = showInList == true ? 1 : 0; + param["InsigniaID"] = insigniaID.ToString(); + param["MembershipFee"] = membershipFee; + param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; + param["AllowPublish"] = allowPublish == true ? 1 : 0; + param["MaturePublish"] = maturePublish == true ? 1 : 0; + + XmlRpcCall(requestID, "groups.updateGroup", param); + } + + public void AddGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, + string title, ulong powers) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + param["Name"] = name; + param["Description"] = description; + param["Title"] = title; + param["Powers"] = powers.ToString(); + + XmlRpcCall(requestID, "groups.addRoleToGroup", param); + } + + public void RemoveGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + + XmlRpcCall(requestID, "groups.removeRoleFromGroup", param); + } + + public void UpdateGroupRole(GroupRequestID requestID, UUID groupID, UUID roleID, string name, string description, + string title, ulong powers) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + if (name != null) + { + param["Name"] = name; + } + if (description != null) + { + param["Description"] = description; + } + if (title != null) + { + param["Title"] = title; + } + param["Powers"] = powers.ToString(); + + XmlRpcCall(requestID, "groups.updateGroupRole", param); + } + + public GroupRecord GetGroupRecord(GroupRequestID requestID, UUID GroupID, string GroupName) + { + Hashtable param = new Hashtable(); + if (GroupID != UUID.Zero) + { + param["GroupID"] = GroupID.ToString(); + } + if ((GroupName != null) && (GroupName != string.Empty)) + { + param["Name"] = GroupName.ToString(); + } + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroup", param); + + if (respData.Contains("error")) + { + return null; + } + + return GroupProfileHashtableToGroupRecord(respData); + + } + + public GroupProfileData GetMemberGroupProfile(GroupRequestID requestID, UUID GroupID, UUID AgentID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroup", param); + + if (respData.Contains("error")) + { + // GroupProfileData is not nullable + return new GroupProfileData(); + } + + GroupMembershipData MemberInfo = GetAgentGroupMembership(requestID, AgentID, GroupID); + GroupProfileData MemberGroupProfile = GroupProfileHashtableToGroupProfileData(respData); + + MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle; + MemberGroupProfile.PowersMask = MemberInfo.GroupPowers; + + return MemberGroupProfile; + + } + + + + public void SetAgentActiveGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + XmlRpcCall(requestID, "groups.setAgentActiveGroup", param); + } + + public void SetAgentActiveGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["SelectedRoleID"] = RoleID.ToString(); + + XmlRpcCall(requestID, "groups.setAgentGroupInfo", param); + } + + public void SetAgentGroupInfo(GroupRequestID requestID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["AcceptNotices"] = AcceptNotices ? "1" : "0"; + param["ListInProfile"] = ListInProfile ? "1" : "0"; + + XmlRpcCall(requestID, "groups.setAgentGroupInfo", param); + + } + + public void AddAgentToGroupInvite(GroupRequestID requestID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + param["AgentID"] = agentID.ToString(); + param["RoleID"] = roleID.ToString(); + param["GroupID"] = groupID.ToString(); + + XmlRpcCall(requestID, "groups.addAgentToGroupInvite", param); + + } + + public GroupInviteInfo GetAgentToGroupInvite(GroupRequestID requestID, UUID inviteID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getAgentToGroupInvite", param); + + if (respData.Contains("error")) + { + return null; + } + + GroupInviteInfo inviteInfo = new GroupInviteInfo(); + inviteInfo.InviteID = inviteID; + inviteInfo.GroupID = UUID.Parse((string)respData["GroupID"]); + inviteInfo.RoleID = UUID.Parse((string)respData["RoleID"]); + inviteInfo.AgentID = UUID.Parse((string)respData["AgentID"]); + + return inviteInfo; + } + + public void RemoveAgentToGroupInvite(GroupRequestID requestID, UUID inviteID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + + XmlRpcCall(requestID, "groups.removeAgentToGroupInvite", param); + } + + public void AddAgentToGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + XmlRpcCall(requestID, "groups.addAgentToGroup", param); + } + + public void RemoveAgentFromGroup(GroupRequestID requestID, UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + XmlRpcCall(requestID, "groups.removeAgentFromGroup", param); + } + + public void AddAgentToGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + XmlRpcCall(requestID, "groups.addAgentToGroupRole", param); + } + + public void RemoveAgentFromGroupRole(GroupRequestID requestID, UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + XmlRpcCall(requestID, "groups.removeAgentFromGroupRole", param); + } + + + public List FindGroups(GroupRequestID requestID, string search) + { + Hashtable param = new Hashtable(); + param["Search"] = search; + + Hashtable respData = XmlRpcCall(requestID, "groups.findGroups", param); + + List findings = new List(); + + if (!respData.Contains("error")) + { + Hashtable results = (Hashtable)respData["results"]; + foreach (Hashtable groupFind in results.Values) + { + DirGroupsReplyData data = new DirGroupsReplyData(); + data.groupID = new UUID((string)groupFind["GroupID"]); ; + data.groupName = (string)groupFind["Name"]; + data.members = int.Parse((string)groupFind["Members"]); + // data.searchOrder = order; + + findings.Add(data); + } + } + + return findings; + } + + public GroupMembershipData GetAgentGroupMembership(GroupRequestID requestID, UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMembership", param); + + if (respData.Contains("error")) + { + return null; + } + + GroupMembershipData data = HashTableToGroupMembershipData(respData); + + return data; + } + + public GroupMembershipData GetAgentActiveMembership(GroupRequestID requestID, UUID AgentID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getAgentActiveMembership", param); + + if (respData.Contains("error")) + { + return null; + } + + return HashTableToGroupMembershipData(respData); + } + + + public List GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMemberships", param); + + List memberships = new List(); + + if (!respData.Contains("error")) + { + foreach (object membership in respData.Values) + { + memberships.Add(HashTableToGroupMembershipData((Hashtable)membership)); + } + } + + return memberships; + } + + public List GetAgentGroupRoles(GroupRequestID requestID, UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getAgentRoles", param); + + List Roles = new List(); + + if (respData.Contains("error")) + { + return Roles; + } + + foreach (Hashtable role in respData.Values) + { + GroupRolesData data = new GroupRolesData(); + data.RoleID = new UUID((string)role["RoleID"]); + data.Name = (string)role["Name"]; + data.Description = (string)role["Description"]; + data.Powers = ulong.Parse((string)role["Powers"]); + data.Title = (string)role["Title"]; + + Roles.Add(data); + } + + return Roles; + + + } + + public List GetGroupRoles(GroupRequestID requestID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoles", param); + + List Roles = new List(); + + if (respData.Contains("error")) + { + return Roles; + } + + foreach (Hashtable role in respData.Values) + { + GroupRolesData data = new GroupRolesData(); + data.Description = (string)role["Description"]; + data.Members = int.Parse((string)role["Members"]); + data.Name = (string)role["Name"]; + data.Powers = ulong.Parse((string)role["Powers"]); + data.RoleID = new UUID((string)role["RoleID"]); + data.Title = (string)role["Title"]; + + Roles.Add(data); + } + + return Roles; + + } + + + + public List GetGroupMembers(GroupRequestID requestID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroupMembers", param); + + List members = new List(); + + if (respData.Contains("error")) + { + return members; + } + + foreach (Hashtable membership in respData.Values) + { + GroupMembersData data = new GroupMembersData(); + + data.AcceptNotices = ((string)membership["AcceptNotices"]) == "1"; + data.AgentID = new UUID((string)membership["AgentID"]); + data.Contribution = int.Parse((string)membership["Contribution"]); + data.IsOwner = ((string)membership["IsOwner"]) == "1"; + data.ListInProfile = ((string)membership["ListInProfile"]) == "1"; + data.AgentPowers = ulong.Parse((string)membership["AgentPowers"]); + data.Title = (string)membership["Title"]; + + members.Add(data); + } + + return members; + + } + + public List GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoleMembers", param); + + List members = new List(); + + if (!respData.Contains("error")) + { + foreach (Hashtable membership in respData.Values) + { + GroupRoleMembersData data = new GroupRoleMembersData(); + + data.MemberID = new UUID((string)membership["AgentID"]); + data.RoleID = new UUID((string)membership["RoleID"]); + + members.Add(data); + } + } + return members; + } + + public List GetGroupNotices(GroupRequestID requestID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotices", param); + + List values = new List(); + + if (!respData.Contains("error")) + { + foreach (Hashtable value in respData.Values) + { + GroupNoticeData data = new GroupNoticeData(); + data.NoticeID = UUID.Parse((string)value["NoticeID"]); + data.Timestamp = uint.Parse((string)value["Timestamp"]); + data.FromName = (string)value["FromName"]; + data.Subject = (string)value["Subject"]; + data.HasAttachment = false; + data.AssetType = 0; + + values.Add(data); + } + } + return values; + + } + public GroupNoticeInfo GetGroupNotice(GroupRequestID requestID, UUID noticeID) + { + Hashtable param = new Hashtable(); + param["NoticeID"] = noticeID.ToString(); + + Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotice", param); + + + if (respData.Contains("error")) + { + return null; + } + + GroupNoticeInfo data = new GroupNoticeInfo(); + data.GroupID = UUID.Parse((string)respData["GroupID"]); + data.Message = (string)respData["Message"]; + data.BinaryBucket = Utils.HexStringToBytes((string)respData["BinaryBucket"], true); + data.noticeData.NoticeID = UUID.Parse((string)respData["NoticeID"]); + data.noticeData.Timestamp = uint.Parse((string)respData["Timestamp"]); + data.noticeData.FromName = (string)respData["FromName"]; + data.noticeData.Subject = (string)respData["Subject"]; + data.noticeData.HasAttachment = false; + data.noticeData.AssetType = 0; + + if (data.Message == null) + { + data.Message = string.Empty; + } + + return data; + } + public void AddGroupNotice(GroupRequestID requestID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) + { + string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, ""); + + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["NoticeID"] = noticeID.ToString(); + param["FromName"] = fromName; + param["Subject"] = subject; + param["Message"] = message; + param["BinaryBucket"] = binBucket; + param["TimeStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); + + XmlRpcCall(requestID, "groups.addGroupNotice", param); + } + #endregion + + #region XmlRpcHashtableMarshalling + private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile) + { + GroupProfileData group = new GroupProfileData(); + group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); + group.Name = (string)groupProfile["Name"]; + + if (groupProfile["Charter"] != null) + { + group.Charter = (string)groupProfile["Charter"]; + } + + group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; + group.InsigniaID = UUID.Parse((string)groupProfile["InsigniaID"]); + group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); + group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; + group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; + group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; + group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); + group.OwnerRole = UUID.Parse((string)groupProfile["OwnerRoleID"]); + + group.GroupMembershipCount = int.Parse((string)groupProfile["GroupMembershipCount"]); + group.GroupRolesCount = int.Parse((string)groupProfile["GroupRolesCount"]); + + return group; + } + + private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile) + { + + GroupRecord group = new GroupRecord(); + group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); + group.GroupName = groupProfile["Name"].ToString(); + if (groupProfile["Charter"] != null) + { + group.Charter = (string)groupProfile["Charter"]; + } + group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; + group.GroupPicture = UUID.Parse((string)groupProfile["InsigniaID"]); + group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); + group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; + group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; + group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; + group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); + group.OwnerRoleID = UUID.Parse((string)groupProfile["OwnerRoleID"]); + + return group; + } + private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData) + { + GroupMembershipData data = new GroupMembershipData(); + data.AcceptNotices = ((string)respData["AcceptNotices"] == "1"); + data.Contribution = int.Parse((string)respData["Contribution"]); + data.ListInProfile = ((string)respData["ListInProfile"] == "1"); + + data.ActiveRole = new UUID((string)respData["SelectedRoleID"]); + data.GroupTitle = (string)respData["Title"]; + + data.GroupPowers = ulong.Parse((string)respData["GroupPowers"]); + + // Is this group the agent's active group + + data.GroupID = new UUID((string)respData["GroupID"]); + + UUID ActiveGroup = new UUID((string)respData["ActiveGroupID"]); + data.Active = data.GroupID.Equals(ActiveGroup); + + data.AllowPublish = ((string)respData["AllowPublish"] == "1"); + if (respData["Charter"] != null) + { + data.Charter = (string)respData["Charter"]; + } + data.FounderID = new UUID((string)respData["FounderID"]); + data.GroupID = new UUID((string)respData["GroupID"]); + data.GroupName = (string)respData["GroupName"]; + data.GroupPicture = new UUID((string)respData["InsigniaID"]); + data.MaturePublish = ((string)respData["MaturePublish"] == "1"); + data.MembershipFee = int.Parse((string)respData["MembershipFee"]); + data.OpenEnrollment = ((string)respData["OpenEnrollment"] == "1"); + data.ShowInList = ((string)respData["ShowInList"] == "1"); + return data; + } + + #endregion + + /// + /// Encapsulate the XmlRpc call to standardize security and error handling. + /// + private Hashtable XmlRpcCall(GroupRequestID requestID, string function, Hashtable param) + { + if (requestID == null) + { + requestID = new GroupRequestID(); + } + param.Add("RequestingAgentID", requestID.AgentID.ToString()); + param.Add("RequestingAgentUserService", requestID.UserServiceURL); + param.Add("RequestingSessionID", requestID.SessionID.ToString()); + + + param.Add("ReadKey", m_groupReadKey); + param.Add("WriteKey", m_groupWriteKey); + + + IList parameters = new ArrayList(); + parameters.Add(param); + + XmlRpcRequest req; + if (!m_disableKeepAlive) + { + req = new XmlRpcRequest(function, parameters); + } + else + { + // This seems to solve a major problem on some windows servers + req = new NoKeepAliveXmlRpcRequest(function, parameters); + } + + XmlRpcResponse resp = null; + + try + { + resp = req.Send(m_serviceURL, 10000); + } + catch (Exception e) + { + m_log.ErrorFormat("[XMLRPCGROUPDATA]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", e.ToString()); + + + foreach (string key in param.Keys) + { + m_log.WarnFormat("[XMLRPCGROUPDATA]: {0} :: {1}", key, param[key].ToString()); + } + + Hashtable respData = new Hashtable(); + respData.Add("error", e.ToString()); + return respData; + } + + if (resp.Value is Hashtable) + { + Hashtable respData = (Hashtable)resp.Value; + if (respData.Contains("error") && !respData.Contains("succeed")) + { + LogRespDataToConsoleError(respData); + } + + return respData; + } + + m_log.ErrorFormat("[XMLRPCGROUPDATA]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); + + if (resp.Value is ArrayList) + { + ArrayList al = (ArrayList)resp.Value; + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Contains {0} elements", al.Count); + + foreach (object o in al) + { + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} :: {1}", o.GetType().ToString(), o.ToString()); + } + } + else + { + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Function returned: {0}", resp.Value.ToString()); + } + + Hashtable error = new Hashtable(); + error.Add("error", "invalid return value"); + return error; + } + + private void LogRespDataToConsoleError(Hashtable respData) + { + m_log.Error("[XMLRPCGROUPDATA]: Error:"); + + foreach (string key in respData.Keys) + { + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Key: {0}", key); + + string[] lines = respData[key].ToString().Split(new char[] { '\n' }); + foreach (string line in lines) + { + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0}", line); + } + + } + } + + + } + + public class GroupNoticeInfo + { + public GroupNoticeData noticeData = new GroupNoticeData(); + public UUID GroupID = UUID.Zero; + public string Message = string.Empty; + public byte[] BinaryBucket = new byte[0]; + } +} + +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + using System.Net; + using System.Text; + using System.Reflection; + + /// Class supporting the request side of an XML-RPC transaction. + public class NoKeepAliveXmlRpcRequest : XmlRpcRequest + { + private Encoding _encoding = new ASCIIEncoding(); + private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer(); + private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer(); + + /// Instantiate an XmlRpcRequest for a specified method and parameters. + /// String designating the object.method on the server the request + /// should be directed to. + /// ArrayList of XML-RPC type parameters to invoke the request with. + public NoKeepAliveXmlRpcRequest(String methodName, IList parameters) + { + MethodName = methodName; + _params = parameters; + } + + /// Send the request to the server. + /// String The url of the XML-RPC server. + /// XmlRpcResponse The response generated. + public XmlRpcResponse Send(String url) + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if (request == null) + throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR, + XmlRpcErrorCodes.TRANSPORT_ERROR_MSG + ": Could not create request with " + url); + request.Method = "POST"; + request.ContentType = "text/xml"; + request.AllowWriteStreamBuffering = true; + request.KeepAlive = false; + + Stream stream = request.GetRequestStream(); + XmlTextWriter xml = new XmlTextWriter(stream, _encoding); + _serializer.Serialize(xml, this); + xml.Flush(); + xml.Close(); + + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + StreamReader input = new StreamReader(response.GetResponseStream()); + + XmlRpcResponse resp = (XmlRpcResponse)_deserializer.Deserialize(input); + input.Close(); + response.Close(); + return resp; + } + } +} -- cgit v1.1