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<Scene> m_sceneList = new List<Scene>();
+
+        private IMessageTransferModule m_msgTransferModule = null;
+
+        private IGroupsModule m_groupsModule = null;
+
+        // TODO: Move this off to the xmlrpc server
+        public Dictionary<Guid, List<Guid>> m_agentsInGroupSession = new Dictionary<Guid, List<Guid>>();
+        public Dictionary<Guid, List<Guid>> m_agentsDroppedSession = new Dictionary<Guid, List<Guid>>();
+
+
+        // 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<IGroupsModule>();
+
+            // 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<IMessageTransferModule>();
+
+            // 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<IEventQueue>();
+                                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<Guid>());
+                m_agentsDroppedSession.Add(sessionID, new List<Guid>());
+            }
+        }
+
+        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<IEventQueue>();
+                    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<IEventQueue>();
+
+            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
+
+        /// <summary>
+        /// Try to find an active IClientAPI reference for agentID giving preference to root connections
+        /// </summary>
+        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
+    {
+        /// <summary>
+        /// ; 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
+        /// </summary>
+
+        private static readonly ILog m_log =
+            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+        private List<Scene> m_sceneList = new List<Scene>();
+
+        private IMessageTransferModule m_msgTransferModule = null;
+        
+        private IGroupsServicesConnector m_groupData = null;
+
+        class GroupRequestIDInfo
+        {
+            public GroupRequestID RequestID = new GroupRequestID();
+            public DateTime LastUsedTMStamp = DateTime.MinValue;
+        }
+        private Dictionary<UUID, GroupRequestIDInfo> m_clientRequestIDInfo = new Dictionary<UUID, GroupRequestIDInfo>();
+        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<IGroupsModule>(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<IGroupsServicesConnector>();
+
+                // 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<IMessageTransferModule>();
+
+                // 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);
+        }
+
+        /// <summary>
+        /// Get the Role Titles for an Agent, for a specific group
+        /// </summary>
+        public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
+        {
+            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+            GroupRequestID grID = GetClientGroupRequestID(remoteClient);
+
+            List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(grID, remoteClient.AgentId, groupID);
+            GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(grID, remoteClient.AgentId, groupID);
+
+            List<GroupTitlesData> titles = new List<GroupTitlesData>();
+            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<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
+        {
+            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+            List<GroupMembersData> 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<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
+        {
+            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+            List<GroupRolesData> 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<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
+        {
+            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+            List<GroupRoleMembersData> 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<IMoneyModule>();
+            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();
+        }
+
+        /// <summary>
+        /// Get the title of the agent's current role.
+        /// </summary>
+        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;
+        }
+
+        /// <summary>
+        /// Change the current Active Group Role for Agent
+        /// </summary>
+        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
+
+        /// <summary>
+        /// Try to find an active IClientAPI reference for agentID giving preference to root connections
+        /// </summary>
+        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;
+        }
+
+        /// <summary>
+        /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
+        /// </summary>
+        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<IEventQueue>();
+
+            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();
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Send updates to all clients who might be interested in groups data for dataForClientID
+        /// </summary>
+        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); });
+                }
+            }
+        }
+
+        /// <summary>
+        /// Update remoteClient with group information about dataForAgentID
+        /// </summary>
+        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<DirGroupsReplyData> FindGroups(GroupRequestID requestID, string search);
-        List<GroupMembersData> 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<GroupRolesData> GetGroupRoles(GroupRequestID requestID, UUID GroupID);
-        List<GroupRoleMembersData> 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<GroupRolesData> 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<GroupMembershipData> 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<GroupNoticeData> 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<DirGroupsReplyData> FindGroups(GroupRequestID requestID, string search);
+        List<GroupMembersData> 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<GroupRolesData> GetGroupRoles(GroupRequestID requestID, UUID GroupID);
+        List<GroupRoleMembersData> 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<GroupRolesData> 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<GroupMembershipData> 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<GroupNoticeData> 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;
-        }
-
-        /// <summary>
-        /// 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.
-        /// </summary>
-        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<DirGroupsReplyData> FindGroups(GroupRequestID requestID, string search)
-        {
-            Hashtable param = new Hashtable();
-            param["Search"] = search;
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.findGroups", param);
-
-            List<DirGroupsReplyData> findings = new List<DirGroupsReplyData>();
-
-            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<GroupMembershipData> GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID)
-        {
-            Hashtable param = new Hashtable();
-            param["AgentID"] = AgentID.ToString();
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMemberships", param);
-
-            List<GroupMembershipData> memberships = new List<GroupMembershipData>();
-
-            if (!respData.Contains("error"))
-            {
-                foreach (object membership in respData.Values)
-                {
-                    memberships.Add(HashTableToGroupMembershipData((Hashtable)membership));
-                }
-            }
-            
-            return memberships;
-        }
-
-        public List<GroupRolesData> 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<GroupRolesData> Roles = new List<GroupRolesData>();
-
-            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<GroupRolesData> GetGroupRoles(GroupRequestID requestID, UUID GroupID)
-        {
-            Hashtable param = new Hashtable();
-            param["GroupID"] = GroupID.ToString();
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoles", param);
-
-            List<GroupRolesData> Roles = new List<GroupRolesData>();
-
-            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<GroupMembersData> GetGroupMembers(GroupRequestID requestID, UUID GroupID)
-        {
-            Hashtable param = new Hashtable();
-            param["GroupID"] = GroupID.ToString();
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupMembers", param);
-
-            List<GroupMembersData> members = new List<GroupMembersData>();
-
-            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<GroupRoleMembersData> GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID)
-        {
-            Hashtable param = new Hashtable();
-            param["GroupID"] = GroupID.ToString();
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoleMembers", param);
-
-            List<GroupRoleMembersData> members = new List<GroupRoleMembersData>();
-
-            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<GroupNoticeData> GetGroupNotices(GroupRequestID requestID, UUID GroupID)
-        {
-            Hashtable param = new Hashtable();
-            param["GroupID"] = GroupID.ToString();
-
-            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotices", param);
-
-            List<GroupNoticeData> values = new List<GroupNoticeData>();
-
-            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;
-
-    /// <summary>Class supporting the request side of an XML-RPC transaction.</summary>
-    public class NoKeepAliveXmlRpcRequest : XmlRpcRequest
-    {
-        private Encoding _encoding = new ASCIIEncoding();
-        private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer();
-        private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer();
-
-        /// <summary>Instantiate an <c>XmlRpcRequest</c> for a specified method and parameters.</summary>
-        /// <param name="methodName"><c>String</c> designating the <i>object.method</i> on the server the request
-        /// should be directed to.</param>
-        /// <param name="parameters"><c>ArrayList</c> of XML-RPC type parameters to invoke the request with.</param>
-        public NoKeepAliveXmlRpcRequest(String methodName, IList parameters)
-        {
-            MethodName = methodName;
-            _params = parameters;
-        }
-
-        /// <summary>Send the request to the server.</summary>
-        /// <param name="url"><c>String</c> The url of the XML-RPC server.</param>
-        /// <returns><c>XmlRpcResponse</c> The response generated.</returns>
-        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<Scene> m_sceneList = new List<Scene>();
-
-        private IMessageTransferModule m_msgTransferModule = null;
-
-        private IGroupsModule m_groupsModule = null;
-
-        // TODO: Move this off to the xmlrpc server
-        public Dictionary<Guid, List<Guid>> m_agentsInGroupSession = new Dictionary<Guid, List<Guid>>();
-        public Dictionary<Guid, List<Guid>> m_agentsDroppedSession = new Dictionary<Guid, List<Guid>>();
-
-
-        // 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<IGroupsModule>();
-
-            // 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<IMessageTransferModule>();
-
-            // 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<IEventQueue>();
-                                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<Guid>());
-                m_agentsDroppedSession.Add(sessionID, new List<Guid>());
-            }
-        }
-
-        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<IEventQueue>();
-                    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<IEventQueue>();
-
-            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
-
-        /// <summary>
-        /// Try to find an active IClientAPI reference for agentID giving preference to root connections
-        /// </summary>
-        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
-    {
-        /// <summary>
-        /// ; 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
-        /// </summary>
-
-        private static readonly ILog m_log =
-            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        private List<Scene> m_sceneList = new List<Scene>();
-
-        private IMessageTransferModule m_msgTransferModule = null;
-        
-        private IGroupDataProvider m_groupData = null;
-
-        class GroupRequestIDInfo
-        {
-            public GroupRequestID RequestID = new GroupRequestID();
-            public DateTime LastUsedTMStamp = DateTime.MinValue;
-        }
-        private Dictionary<UUID, GroupRequestIDInfo> m_clientRequestIDInfo = new Dictionary<UUID, GroupRequestIDInfo>();
-        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<IGroupsModule>(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<IMessageTransferModule>();
-
-                // 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);
-        }
-
-        /// <summary>
-        /// Get the Role Titles for an Agent, for a specific group
-        /// </summary>
-        public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
-        {
-            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
-
-            GroupRequestID grID = GetClientGroupRequestID(remoteClient);
-
-            List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(grID, remoteClient.AgentId, groupID);
-            GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(grID, remoteClient.AgentId, groupID);
-
-            List<GroupTitlesData> titles = new List<GroupTitlesData>();
-            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<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
-        {
-            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
-
-            List<GroupMembersData> 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<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
-        {
-            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
-
-            List<GroupRolesData> 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<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
-        {
-            if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
-
-            List<GroupRoleMembersData> 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<IMoneyModule>();
-            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();
-        }
-
-        /// <summary>
-        /// Get the title of the agent's current role.
-        /// </summary>
-        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;
-        }
-
-        /// <summary>
-        /// Change the current Active Group Role for Agent
-        /// </summary>
-        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
-
-        /// <summary>
-        /// Try to find an active IClientAPI reference for agentID giving preference to root connections
-        /// </summary>
-        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;
-        }
-
-        /// <summary>
-        /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
-        /// </summary>
-        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<IEventQueue>();
-
-            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();
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Send updates to all clients who might be interested in groups data for dataForClientID
-        /// </summary>
-        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); });
-                }
-            }
-        }
-
-        /// <summary>
-        /// Update remoteClient with group information about dataForAgentID
-        /// </summary>
-        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<IGroupsServicesConnector>(this);
+        }
+
+        public void RemoveRegion(OpenSim.Region.Framework.Scenes.Scene scene)
+        {
+            if (scene.RequestModuleInterface<IGroupsServicesConnector>() == this)
+                scene.UnregisterModuleInterface<IGroupsServicesConnector>(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
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        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<DirGroupsReplyData> FindGroups(GroupRequestID requestID, string search)
+        {
+            Hashtable param = new Hashtable();
+            param["Search"] = search;
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.findGroups", param);
+
+            List<DirGroupsReplyData> findings = new List<DirGroupsReplyData>();
+
+            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<GroupMembershipData> GetAgentGroupMemberships(GroupRequestID requestID, UUID AgentID)
+        {
+            Hashtable param = new Hashtable();
+            param["AgentID"] = AgentID.ToString();
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.getAgentGroupMemberships", param);
+
+            List<GroupMembershipData> memberships = new List<GroupMembershipData>();
+
+            if (!respData.Contains("error"))
+            {
+                foreach (object membership in respData.Values)
+                {
+                    memberships.Add(HashTableToGroupMembershipData((Hashtable)membership));
+                }
+            }
+            
+            return memberships;
+        }
+
+        public List<GroupRolesData> 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<GroupRolesData> Roles = new List<GroupRolesData>();
+
+            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<GroupRolesData> GetGroupRoles(GroupRequestID requestID, UUID GroupID)
+        {
+            Hashtable param = new Hashtable();
+            param["GroupID"] = GroupID.ToString();
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoles", param);
+
+            List<GroupRolesData> Roles = new List<GroupRolesData>();
+
+            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<GroupMembersData> GetGroupMembers(GroupRequestID requestID, UUID GroupID)
+        {
+            Hashtable param = new Hashtable();
+            param["GroupID"] = GroupID.ToString();
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupMembers", param);
+
+            List<GroupMembersData> members = new List<GroupMembersData>();
+
+            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<GroupRoleMembersData> GetGroupRoleMembers(GroupRequestID requestID, UUID GroupID)
+        {
+            Hashtable param = new Hashtable();
+            param["GroupID"] = GroupID.ToString();
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupRoleMembers", param);
+
+            List<GroupRoleMembersData> members = new List<GroupRoleMembersData>();
+
+            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<GroupNoticeData> GetGroupNotices(GroupRequestID requestID, UUID GroupID)
+        {
+            Hashtable param = new Hashtable();
+            param["GroupID"] = GroupID.ToString();
+
+            Hashtable respData = XmlRpcCall(requestID, "groups.getGroupNotices", param);
+
+            List<GroupNoticeData> values = new List<GroupNoticeData>();
+
+            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
+
+        /// <summary>
+        /// Encapsulate the XmlRpc call to standardize security and error handling.
+        /// </summary>
+        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;
+
+    /// <summary>Class supporting the request side of an XML-RPC transaction.</summary>
+    public class NoKeepAliveXmlRpcRequest : XmlRpcRequest
+    {
+        private Encoding _encoding = new ASCIIEncoding();
+        private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer();
+        private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer();
+
+        /// <summary>Instantiate an <c>XmlRpcRequest</c> for a specified method and parameters.</summary>
+        /// <param name="methodName"><c>String</c> designating the <i>object.method</i> on the server the request
+        /// should be directed to.</param>
+        /// <param name="parameters"><c>ArrayList</c> of XML-RPC type parameters to invoke the request with.</param>
+        public NoKeepAliveXmlRpcRequest(String methodName, IList parameters)
+        {
+            MethodName = methodName;
+            _params = parameters;
+        }
+
+        /// <summary>Send the request to the server.</summary>
+        /// <param name="url"><c>String</c> The url of the XML-RPC server.</param>
+        /// <returns><c>XmlRpcResponse</c> The response generated.</returns>
+        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