diff --git a/OpenSim/Framework/GridInstantMessage.cs b/OpenSim/Framework/GridInstantMessage.cs index 037f110dca..4ca4e67435 100644 --- a/OpenSim/Framework/GridInstantMessage.cs +++ b/OpenSim/Framework/GridInstantMessage.cs @@ -33,26 +33,60 @@ namespace OpenSim.Framework [Serializable] public class GridInstantMessage { - public byte[] binaryBucket; - public byte dialog; public Guid fromAgentID; public string fromAgentName; public Guid fromAgentSession; + public Guid toAgentID; + public byte dialog; public bool fromGroup; - public Guid imSessionID; - public string message; + public Guid imSessionID; public byte offline; + public Vector3 Position; + public byte[] binaryBucket; + public uint ParentEstateID; - - public Vector3 Position; - public Guid RegionID; public uint timestamp; - public Guid toAgentID; public GridInstantMessage() + { + binaryBucket = new byte[0]; + } + + public GridInstantMessage(IScene scene, UUID _fromAgentID, + string _fromAgentName, UUID _fromAgentSession, UUID _toAgentID, + byte _dialog, bool _fromGroup, string _message, + UUID _imSessionID, bool _offline, Vector3 _position, + byte[] _binaryBucket) + { + fromAgentID = _fromAgentID.Guid; + fromAgentName = _fromAgentName; + fromAgentSession = _fromAgentSession.Guid; + toAgentID = _toAgentID.Guid; + dialog = _dialog; + fromGroup = _fromGroup; + message = _message; + imSessionID = _imSessionID.Guid; + if (_offline) + offline = 1; + else + offline = 0; + Position = _position; + binaryBucket = _binaryBucket; + + ParentEstateID = scene.RegionInfo.EstateSettings.ParentEstateID; + RegionID = scene.RegionInfo.RegionSettings.RegionUUID.Guid; + timestamp = (uint)Util.UnixTimeSinceEpoch(); + } + + public GridInstantMessage(IScene scene, UUID _fromAgentID, + string _fromAgentName, UUID _toAgentID, byte _dialog, + string _message, bool _offline, + Vector3 _position) : this(scene, _fromAgentID, _fromAgentName, + UUID.Zero, _toAgentID, _dialog, false, _message, + _fromAgentID ^ _toAgentID, _offline, _position, new byte[0]) { } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index ea82a374f6..3aa3fea273 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4023,6 +4023,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.ImprovedInstantMessage: ImprovedInstantMessagePacket msgpack = (ImprovedInstantMessagePacket)Pack; + Console.WriteLine(msgpack.ToString()); string IMfromName = Util.FieldToString(msgpack.MessageBlock.FromAgentName); string IMmessage = Utils.BytesToString(msgpack.MessageBlock.Message); handlerInstantMessage = OnInstantMessage; @@ -6171,7 +6172,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP case PacketType.ObjectBuy: ObjectBuyPacket objectBuyPacket = (ObjectBuyPacket)Pack; handlerObjectBuy = OnObjectBuy; - Console.WriteLine(objectBuyPacket.ToString()); + if (handlerObjectBuy != null) { foreach (ObjectBuyPacket.ObjectDataBlock d diff --git a/OpenSim/Region/Environment/InstantMessageReceiver.cs b/OpenSim/Region/Environment/InstantMessageReceiver.cs deleted file mode 100644 index d82bffcfbd..0000000000 --- a/OpenSim/Region/Environment/InstantMessageReceiver.cs +++ /dev/null @@ -1,54 +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 OpenSim Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; - -namespace OpenSim.Region.Environment -{ - /// - /// Bit Vector for Which Modules to send an instant message to from the Scene or an Associated Module - /// - - // This prevents the Modules from sending Instant messages to other modules through the scene - // and then receiving the same messages - - // This is mostly here because on LLSL and the SecondLife Client, IMs,Groups and friends are linked - // inseparably - - [Flags] - public enum InstantMessageReceiver : uint - { - /// None of them.. here for posterity and amusement - None = 0, - /// The IM Module - IMModule = 0x00000001, - /// The Friends Module - FriendsModule = 0x00000002, - /// The Groups Module - GroupsModule = 0x00000004 - } -} diff --git a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs index 58251cbb4a..72c64add0a 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs @@ -36,6 +36,7 @@ using Nwc.XmlRpc; using OpenSim.Framework; using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Servers; +using OpenSim.Region.Interfaces; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; @@ -105,6 +106,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends private Scene m_initialScene; // saves a lookup if we don't have a specific scene private Dictionary m_scenes = new Dictionary(); + private IMessageTransferModule m_TransferModule = null; #region IRegionModule Members @@ -124,7 +126,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends m_scenes[scene.RegionInfo.RegionHandle] = scene; } scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnGridInstantMessage += OnGridInstantMessage; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; scene.EventManager.OnMakeChildAgent += MakeChildAgent; scene.EventManager.OnClientClosed += ClientClosed; @@ -132,6 +134,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends public void PostInitialise() { + List scenes = new List(m_scenes.Values); + m_TransferModule = scenes[0].RequestModuleInterface(); + if (m_TransferModule == null) + m_log.Error("[FRIENDS]: Unable to find a message transfer module, friendship offers will not work"); } public void Close() @@ -434,11 +440,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends // we don't want to get that new IM into here if we aren't local, as only on the destination // should receive it. If we *are* local, *we* are the destination, so we have to receive it. // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. - InstantMessageReceiver recv = InstantMessageReceiver.IMModule; - if (GetAnyPresenceFromAgentID(toAgentID) != null) recv |= InstantMessageReceiver.FriendsModule; // We don't really care which local scene we pipe it through. - m_initialScene.TriggerGridInstantMessage(msg, recv); + if (m_TransferModule != null) + { + m_TransferModule.SendInstantMessage(msg, + delegate(bool success) {} + ); + } } else { @@ -531,17 +540,20 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends } } - private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule) + private void OnGridInstantMessage(GridInstantMessage msg) { - if ((whichModule & InstantMessageReceiver.FriendsModule) == 0) - return; - - // Trigger the above event handler - OnInstantMessage(null, new UUID(msg.fromAgentID), new UUID(msg.fromAgentSession), - new UUID(msg.toAgentID), new UUID(msg.imSessionID), msg.timestamp, msg.fromAgentName, - msg.message, msg.dialog, msg.fromGroup, msg.offline, msg.ParentEstateID, - new Vector3(msg.Position.X, msg.Position.Y, msg.Position.Z), new UUID(msg.RegionID), - msg.binaryBucket); + // Just call the IM handler above + // This event won't be raised unless we have that agent, + // so we can depend on the above not trying to send + // via grid again + // + OnInstantMessage(null, new UUID(msg.fromAgentID), + new UUID(msg.fromAgentSession), + new UUID(msg.toAgentID), new UUID(msg.imSessionID), + msg.timestamp, msg.fromAgentName, msg.message, + msg.dialog, msg.fromGroup, msg.offline, + msg.ParentEstateID, msg.Position, + new UUID(msg.RegionID), msg.binaryBucket); } private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List callingCardFolders) @@ -607,12 +619,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends // we don't want to get that new IM into here if we aren't local, as only on the destination // should receive it. If we *are* local, *we* are the destination, so we have to receive it. // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. - InstantMessageReceiver recv = InstantMessageReceiver.IMModule; - if (GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule; // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler // of the type 39 IM - SceneAgentIn.TriggerGridInstantMessage(msg, recv); + if (m_TransferModule != null) + { + m_TransferModule.SendInstantMessage(msg, + delegate(bool success) {} + ); + } // tell client that new friend is online client.SendAgentOnline(new UUID[] { friendID }); @@ -664,12 +679,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends // we don't want to get that new IM into here if we aren't local, as only on the destination // should receive it. If we *are* local, *we* are the destination, so we have to receive it. // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. - InstantMessageReceiver recv = InstantMessageReceiver.IMModule; - if (GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule; // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler // of the type 39 IM - SceneAgentIn.TriggerGridInstantMessage(msg, recv); + if (m_TransferModule != null) + { + m_TransferModule.SendInstantMessage(msg, + delegate(bool success) {} + ); + } } private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) diff --git a/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs index d9a5393232..c291c169df 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs @@ -99,7 +99,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Groups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnClientClosed += OnClientClosed; - scene.EventManager.OnGridInstantMessage += + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; } @@ -187,11 +187,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Groups { } - private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule) + private void OnGridInstantMessage(GridInstantMessage msg) { - if ((whichModule & InstantMessageReceiver.GroupsModule) == 0) - return; - // Trigger the above event handler OnInstantMessage(null, new UUID(msg.fromAgentID), new UUID(msg.fromAgentSession), diff --git a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs index 805f7cb8b2..1b7eb97eb5 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs @@ -36,6 +36,7 @@ using Nini.Config; using Nwc.XmlRpc; using OpenSim.Framework; using OpenSim.Framework.Client; +using OpenSim.Region.Interfaces; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; @@ -46,12 +47,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private readonly List m_scenes = new List(); - private Dictionary m_userRegionMap = new Dictionary(); #region IRegionModule Members private bool gridmode = false; + private IMessageTransferModule m_TransferModule = null; public void Initialise(Scene scene, IConfigSource config) { @@ -65,18 +66,11 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage lock (m_scenes) { - if (m_scenes.Count == 0) - { - //scene.AddXmlRPCHandler("avatar_location_update", processPresenceUpdate); - scene.AddXmlRPCHandler("grid_instant_message", processXMLRPCGridInstantMessage); - ReadConfig(config); - } - if (!m_scenes.Contains(scene)) { m_scenes.Add(scene); scene.EventManager.OnClientConnect += OnClientConnect; - scene.EventManager.OnGridInstantMessage += OnGridInstantMessage; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; } } } @@ -90,17 +84,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage } } - private void ReadConfig(IConfigSource config) - { - IConfig cnf = config.Configs["Startup"]; - if (cnf != null) - { - gridmode = cnf.GetBoolean("gridmode", false); - } - } - public void PostInitialise() { + m_TransferModule = + m_scenes[0].RequestModuleInterface(); + + if (m_TransferModule == null) + m_log.Error("[INSTANT MESSAGE]: No message transfer module, "+ + "IM will not work!"); } public void Close() @@ -120,639 +111,71 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage #endregion private void OnInstantMessage(IClientAPI client, UUID fromAgentID, - UUID fromAgentSession, UUID toAgentID, - UUID imSessionID, uint timestamp, string fromAgentName, - string message, byte dialog, bool fromGroup, byte offline, - uint ParentEstateID, Vector3 Position, UUID RegionID, - byte[] binaryBucket) + UUID fromAgentSession, UUID toAgentID, + UUID imSessionID, uint timestamp, string fromAgentName, + string message, byte dialog, bool fromGroup, byte offline, + uint ParentEstateID, Vector3 Position, UUID RegionID, + byte[] binaryBucket) { - bool dialogHandledElsewhere - = ( dialog == (byte) InstantMessageDialog.FriendshipOffered - || dialog == (byte) InstantMessageDialog.FriendshipAccepted - || dialog == (byte) InstantMessageDialog.FriendshipDeclined - || dialog == (byte) InstantMessageDialog.InventoryOffered - || dialog == (byte) InstantMessageDialog.InventoryAccepted - || dialog == (byte) InstantMessageDialog.InventoryDeclined - || dialog == (byte) InstantMessageDialog.GroupNoticeInventoryAccepted - || dialog == (byte) InstantMessageDialog.GroupNoticeInventoryDeclined - || dialog == (byte) InstantMessageDialog.GroupInvitationAccept - || dialog == (byte) InstantMessageDialog.GroupInvitationDecline - || dialog == (byte) InstantMessageDialog.GroupNotice); - - // IM dialogs need to be pre-processed and have their sessionID filled by the server - // so the sim can match the transaction on the return packet. - - // Don't process IMs that are handled elsewhere (e.g. friend dialog - // IMs) with a non-UUID.Zero agent session, as those have been send - // by a client (either directly or from another region via - // inter-region communication) and will be processed in another - // module (e.g. the friends-module). - // IMs with fromAgentSession == UUID.Zero come from the server, and - // have to be passed to the matching viewer - if (!dialogHandledElsewhere || fromAgentSession == UUID.Zero) + // This module handles exclusively private text IM from user + // to user. All others will be caught in other modules + // + if ( dialog != (byte)InstantMessageDialog.MessageFromAgent + && dialog != (byte)InstantMessageDialog.StartTyping + && dialog != (byte)InstantMessageDialog.StopTyping) { - // Try root avatar only first - foreach (Scene scene in m_scenes) - { - if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence) - { - // Local message - ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; - if (!user.IsChildAgent) - { - user.ControllingClient.SendInstantMessage(fromAgentID, message, - toAgentID, fromAgentName, dialog, - timestamp, imSessionID, fromGroup, binaryBucket); - // Message sent - return; - } - } - } - - // try child avatar second - foreach (Scene scene in m_scenes) - { - if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence) - { - // Local message - ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; - - user.ControllingClient.SendInstantMessage(fromAgentID, message, - toAgentID, fromAgentName, dialog, - timestamp, imSessionID, fromGroup, binaryBucket); - // Message sent - return; - } - } - if (gridmode) - { - // Still here, try send via Grid - - // don't send session drop yet, as it's not reliable somehow. - if (dialog != (byte)InstantMessageDialog.SessionDrop) - { - SendGridInstantMessageViaXMLRPC(client, fromAgentID, - fromAgentSession, toAgentID, - imSessionID, timestamp, fromAgentName, - message, dialog, fromGroup, offline, - ParentEstateID, Position, RegionID, - binaryBucket, getLocalRegionHandleFromUUID(RegionID), 0); - } - } - else - { - if (client != null) - { - if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop) - client.SendInstantMessage(toAgentID, "Unable to send instant message. User is not logged in.", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message"); - } - } + return; } + GridInstantMessage im = new GridInstantMessage(client.Scene, + fromAgentID, fromAgentName, fromAgentSession, toAgentID, + dialog, fromGroup, message, imSessionID, + offline != 0 ? true : false, Position, + binaryBucket); + if (m_TransferModule != null) + { + m_TransferModule.SendInstantMessage(im, + delegate(bool success) + { + if (dialog == (uint)InstantMessageDialog.StartTyping || + dialog == (uint)InstantMessageDialog.StopTyping) + { + return; + } + + if ((client != null) && !success) + { + client.SendInstantMessage(toAgentID, + "Unable to send instant message. "+ + "User is not logged in.", + fromAgentID, "System", + (byte)InstantMessageDialog.BusyAutoResponse, + (uint)Util.UnixTimeSinceEpoch()); + } + } + ); + } } - // Trusty OSG1 called method. This method also gets called from the FriendsModule - // Turns out the sim has to send an instant message to the user to get it to show an accepted friend. /// /// /// /// - private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver which) + private void OnGridInstantMessage(GridInstantMessage msg) { - if ((which & InstantMessageReceiver.IMModule) == 0) - return; - - // Trigger the above event handler - OnInstantMessage(null, new UUID(msg.fromAgentID), new UUID(msg.fromAgentSession), - new UUID(msg.toAgentID), new UUID(msg.imSessionID), msg.timestamp, msg.fromAgentName, - msg.message, msg.dialog, msg.fromGroup, msg.offline, msg.ParentEstateID, - new Vector3(msg.Position.X, msg.Position.Y, msg.Position.Z), new UUID(msg.RegionID), - msg.binaryBucket); + // Just call the Text IM handler above + // This event won't be raised unless we have that agent, + // so we can depend on the above not trying to send + // via grid again + // + OnInstantMessage(null, new UUID(msg.fromAgentID), + new UUID(msg.fromAgentSession), + new UUID(msg.toAgentID), new UUID(msg.imSessionID), + msg.timestamp, msg.fromAgentName, msg.message, + msg.dialog, msg.fromGroup, msg.offline, + msg.ParentEstateID, msg.Position, + new UUID(msg.RegionID), msg.binaryBucket); } - - - /// - /// Process a XMLRPC Grid Instant Message - /// - /// XMLRPC parameters from_agent_id from_agent_session to_agent_id im_session_id timestamp - /// from_agent_name message dialog from_group offline parent_estate_id position_x position_y position_z region_id - /// binary_bucket region_handle - /// Nothing much - protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request) - { - bool successful = false; - // various rational defaults - UUID fromAgentID = UUID.Zero; - UUID fromAgentSession = UUID.Zero; - UUID toAgentID = UUID.Zero; - UUID imSessionID = UUID.Zero; - uint timestamp = 0; - string fromAgentName = ""; - string message = ""; - byte dialog = (byte)0; - bool fromGroup = false; - byte offline = (byte)0; - uint ParentEstateID=0; - Vector3 Position = Vector3.Zero; - UUID RegionID = UUID.Zero ; - byte[] binaryBucket = new byte[0]; - - float pos_x = 0; - float pos_y = 0; - float pos_z = 0; - //m_log.Info("Processing IM"); - - - Hashtable requestData = (Hashtable)request.Params[0]; - // Check if it's got all the data - if (requestData.ContainsKey("from_agent_id") && requestData.ContainsKey("from_agent_session") - && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id") - && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name") - && requestData.ContainsKey("message") && requestData.ContainsKey("dialog") - && requestData.ContainsKey("from_group") - && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id") - && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y") - && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") - && requestData.ContainsKey("binary_bucket") && requestData.ContainsKey("region_handle")) - { - // Do the easy way of validating the UUIDs - UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); - UUID.TryParse((string)requestData["from_agent_session"], out fromAgentSession); - UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); - UUID.TryParse((string)requestData["im_session_id"], out imSessionID); - UUID.TryParse((string)requestData["region_id"], out RegionID); - - # region timestamp - try - { - timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]); - } - catch (ArgumentException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - # endregion - - fromAgentName = (string)requestData["from_agent_name"]; - message = (string)requestData["message"]; - - // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them. - string requestData1 = (string)requestData["dialog"]; - if (string.IsNullOrEmpty(requestData1)) - { - dialog = 0; - } - else - { - byte[] dialogdata = Convert.FromBase64String(requestData1); - dialog = dialogdata[0]; - } - - if ((string)requestData["from_group"] == "TRUE") - fromGroup = true; - - string requestData2 = (string)requestData["offline"]; - if (String.IsNullOrEmpty(requestData2)) - { - offline = 0; - } - else - { - byte[] offlinedata = Convert.FromBase64String(requestData2); - offline = offlinedata[0]; - } - - # region ParentEstateID - try - { - ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]); - } - catch (ArgumentException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - # endregion - - # region pos_x - try - { - pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]); - } - catch (ArgumentException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - # endregion - # region pos_y - try - { - pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]); - } - catch (ArgumentException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - # endregion - # region pos_z - try - { - pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]); - } - catch (ArgumentException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - # endregion - - Position = new Vector3(pos_x, pos_y, pos_z); - - string requestData3 = (string)requestData["binary_bucket"]; - if (string.IsNullOrEmpty(requestData3)) - { - binaryBucket = new byte[0]; - } - else - { - binaryBucket = Convert.FromBase64String(requestData3); - } - - // Create a New GridInstantMessageObject the the data - GridInstantMessage gim = new GridInstantMessage(); - gim.fromAgentID = fromAgentID.Guid; - gim.fromAgentName = fromAgentName; - gim.fromAgentSession = fromAgentSession.Guid; - gim.fromGroup = fromGroup; - gim.imSessionID = imSessionID.Guid; - gim.RegionID = RegionID.Guid; - gim.timestamp = timestamp; - gim.toAgentID = toAgentID.Guid; - gim.message = message; - gim.dialog = dialog; - gim.offline = offline; - gim.ParentEstateID = ParentEstateID; - gim.Position = Position; - gim.binaryBucket = binaryBucket; - - - // Trigger the Instant message in the scene. - foreach (Scene scene in m_scenes) - { - if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence) - { - // Local message - ScenePresence user = (ScenePresence)scene.Entities[toAgentID]; - if (!user.IsChildAgent) - { - scene.EventManager.TriggerGridInstantMessage(gim, InstantMessageReceiver.FriendsModule | InstantMessageReceiver.GroupsModule | InstantMessageReceiver.IMModule); - successful = true; - } - } - } - //OnGridInstantMessage(gim); - - } - - //Send response back to region calling if it was successful - // calling region uses this to know when to look up a user's location again. - XmlRpcResponse resp = new XmlRpcResponse(); - Hashtable respdata = new Hashtable(); - if (successful) - respdata["success"] = "TRUE"; - else - respdata["success"] = "FALSE"; - resp.Value = respdata; - - return resp; - } - - #region Asynchronous setup - /// - /// delegate for sending a grid instant message asynchronously - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public delegate void GridInstantMessageDelegate(IClientAPI client, UUID fromAgentID, - UUID fromAgentSession, UUID toAgentID, - UUID imSessionID, uint timestamp, string fromAgentName, - string message, byte dialog, bool fromGroup, byte offline, - uint ParentEstateID, Vector3 Position, UUID RegionID, - byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle); - - private void GridInstantMessageCompleted(IAsyncResult iar) - { - GridInstantMessageDelegate icon = (GridInstantMessageDelegate)iar.AsyncState; - icon.EndInvoke(iar); - } - - - protected virtual void SendGridInstantMessageViaXMLRPC(IClientAPI client, UUID fromAgentID, - UUID fromAgentSession, UUID toAgentID, - UUID imSessionID, uint timestamp, string fromAgentName, - string message, byte dialog, bool fromGroup, byte offline, - uint ParentEstateID, Vector3 Position, UUID RegionID, - byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle) - { - GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; - - d.BeginInvoke(client,fromAgentID, - fromAgentSession,toAgentID, - imSessionID,timestamp, fromAgentName, - message, dialog, fromGroup, offline, - ParentEstateID, Position, RegionID, - binaryBucket, regionhandle, prevRegionHandle, - GridInstantMessageCompleted, - d); - } - - #endregion - - - /// - /// Recursive SendGridInstantMessage over XMLRPC method. The prevRegionHandle contains the last regionhandle tried - /// if it's the same as the user's looked up region handle, then we end the recursive loop - /// - /// - protected virtual void SendGridInstantMessageViaXMLRPCAsync(IClientAPI client, UUID fromAgentID, - UUID fromAgentSession, UUID toAgentID, - UUID imSessionID, uint timestamp, string fromAgentName, - string message, byte dialog, bool fromGroup, byte offline, - uint ParentEstateID, Vector3 Position, UUID RegionID, - byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle) - { - UserAgentData upd = null; - - bool lookupAgent = false; - - lock (m_userRegionMap) - { - if (m_userRegionMap.ContainsKey(toAgentID) && prevRegionHandle == 0) - { - upd = new UserAgentData(); - upd.AgentOnline = true; - upd.Handle = m_userRegionMap[toAgentID]; - - } - else - { - lookupAgent = true; - - - } - } - - // Are we needing to look-up an agent? - if (lookupAgent) - { - // Non-cached user agent lookup. - upd = m_scenes[0].CommsManager.UserService.GetAgentByUUID(toAgentID); - - if (upd != null) - { - // check if we've tried this before.. This is one way to end the recursive loop - if (upd.Handle == prevRegionHandle) - { - m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); - if (client != null) - { - if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop) - client.SendInstantMessage(toAgentID, "Unable to send instant message", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch()); - } - return; - } - } - else - { - m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); - if (client != null) - { - if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop) - client.SendInstantMessage(toAgentID, "Unable to send instant message", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch()); - } - return; - } - } - - if (upd != null) - { - if (upd.AgentOnline) - { - RegionInfo reginfo = m_scenes[0].SceneGridService.RequestNeighbouringRegionInfo(upd.Handle); - if (reginfo != null) - { - GridInstantMessage msg = new GridInstantMessage(); - msg.fromAgentID = fromAgentID.Guid; - msg.fromAgentSession = fromAgentSession.Guid; - msg.toAgentID = toAgentID.Guid; - msg.imSessionID = imSessionID.Guid; - msg.timestamp = timestamp; - msg.fromAgentName = fromAgentName; - msg.message = message; - msg.dialog = dialog; - msg.fromGroup = fromGroup; - msg.offline = offline; - msg.ParentEstateID = ParentEstateID; - msg.Position = Position; - msg.RegionID = RegionID.Guid; - msg.binaryBucket = binaryBucket; - - Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(msg); - msgdata["region_handle"] = getLocalRegionHandleFromUUID(RegionID); - bool imresult = doIMSending(reginfo, msgdata); - if (imresult) - { - // IM delivery successful, so store the Agent's location in our local cache. - lock (m_userRegionMap) - { - if (m_userRegionMap.ContainsKey(toAgentID)) - { - m_userRegionMap[toAgentID] = upd.Handle; - } - else - { - m_userRegionMap.Add(toAgentID, upd.Handle); - } - } - //m_log.Info("[GRID INSTANT MESSAGE]: Successfully sent a message"); - } - else - { - // try again, but lookup user this time. - // Warning, this must call the Async version - // of this method or we'll be making thousands of threads - // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync - // The version that spawns the thread is SendGridInstantMessageViaXMLRPC - - // This is recursive!!!!! - SendGridInstantMessageViaXMLRPCAsync(client, fromAgentID, - fromAgentSession, toAgentID, - imSessionID, timestamp, fromAgentName, - message, dialog, fromGroup, offline, - ParentEstateID, Position, RegionID, - binaryBucket, regionhandle, upd.Handle); - } - - } - } - else - { - // send Agent Offline message - if (client != null) - { - if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop) - client.SendInstantMessage(toAgentID, "Unable to send instant message: Agent Offline", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message"); - } - } - } - else - { - // send Agent doesn't exist message - if (client != null) - client.SendInstantMessage(toAgentID, "Unable to send instant message: Are you sure this agent exists anymore?", fromAgentID, "System", (byte)InstantMessageDialog.MessageFromObject, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message"); - } - - } - - /// - /// This actually does the XMLRPC Request - /// - /// RegionInfo we pull the data out of to send the request to - /// The Instant Message data Hashtable - /// Bool if the message was successfully delivered at the other side. - private bool doIMSending(RegionInfo reginfo, Hashtable xmlrpcdata) - { - - ArrayList SendParams = new ArrayList(); - SendParams.Add(xmlrpcdata); - XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); - try - { - - XmlRpcResponse GridResp = GridReq.Send("http://" + reginfo.ExternalHostName + ":" + reginfo.HttpPort, 3000); - - Hashtable responseData = (Hashtable)GridResp.Value; - - if (responseData.ContainsKey("success")) - { - if ((string)responseData["success"] == "TRUE") - { - return true; - } - else - { - return false; - } - } - else - { - return false; - } - } - catch (WebException e) - { - m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to http://{0}:{1} the host didn't respond ({2})", - reginfo.ExternalHostName, reginfo.HttpPort, e.Message); - } - - return false; - } - - /// - /// Get ulong region handle for region by it's Region UUID. - /// We use region handles over grid comms because there's all sorts of free and cool caching. - /// - /// UUID of region to get the region handle for - /// - private ulong getLocalRegionHandleFromUUID(UUID regionID) - { - ulong returnhandle = 0; - - lock (m_scenes) - { - foreach (Scene sn in m_scenes) - { - if (sn.RegionInfo.RegionID == regionID) - { - returnhandle = sn.RegionInfo.RegionHandle; - break; - } - } - } - return returnhandle; - } - - /// - /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC - /// - /// The GridInstantMessage object - /// Hashtable containing the XMLRPC request - private Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg) - { - Hashtable gim = new Hashtable(); - gim["from_agent_id"] = msg.fromAgentID.ToString(); - gim["from_agent_session"] = msg.fromAgentSession.ToString(); - gim["to_agent_id"] = msg.toAgentID.ToString(); - gim["im_session_id"] = msg.imSessionID.ToString(); - gim["timestamp"] = msg.timestamp.ToString(); - gim["from_agent_name"] = msg.fromAgentName; - gim["message"] = msg.message; - byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog; - gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None); - - if (msg.fromGroup) - gim["from_group"] = "TRUE"; - else - gim["from_group"] = "FALSE"; - byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline; - gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None); - gim["parent_estate_id"] = msg.ParentEstateID.ToString(); - gim["position_x"] = msg.Position.X.ToString(); - gim["position_y"] = msg.Position.Y.ToString(); - gim["position_z"] = msg.Position.Z.ToString(); - gim["region_id"] = msg.RegionID.ToString(); - gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); - return gim; - } - } } diff --git a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs new file mode 100644 index 0000000000..d1543a0872 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs @@ -0,0 +1,626 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Net; +using System.Threading; +using OpenMetaverse; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Region.Interfaces; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.Environment.Scenes; + +namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage +{ + public class MessageTransferModule : IRegionModule, IMessageTransferModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private bool m_Gridmode = false; + private List m_Scenes = new List(); + private Dictionary m_UserRegionMap = new Dictionary(); + + public void Initialise(Scene scene, IConfigSource config) + { + if (config.Configs["Messaging"] != null) + { + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null || cnf.GetString( + "MessageTransferModule", "MessageTransferModule") != + "MessageTransferModule") + return; + + cnf = config.Configs["Startup"]; + if (cnf != null) + m_Gridmode = cnf.GetBoolean("m_Gridmode", false); + + m_Enabled = true; + } + + lock (m_Scenes) + { + if (m_Scenes.Count == 0) + { + scene.AddXmlRPCHandler("grid_instant_message", processXMLRPCGridInstantMessage); + scene.RegisterModuleInterface(this); + } + + m_Scenes.Add(scene); + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "MessageTransferModule"; } + } + + public bool IsSharedModule + { + get { return true; } + } + + public void SendInstantMessage(GridInstantMessage im, MessageResultNotification result) + { + UUID toAgentID = new UUID(im.toAgentID); + + m_log.DebugFormat("[INSTANT MESSAGE]: Attempting delivery of IM fromn {0} to {1}", im.fromAgentName, toAgentID.ToString()); + + // Try root avatar only first + foreach (Scene scene in m_Scenes) + { + if (scene.Entities.ContainsKey(toAgentID) && + scene.Entities[toAgentID] is ScenePresence) + { + m_log.DebugFormat("[INSTANT MESSAGE]: Looking for {0} in {1}", toAgentID.ToString(), scene.RegionInfo.RegionName); + // Local message + ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; + if (!user.IsChildAgent) + { + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering to client"); + user.ControllingClient.SendInstantMessage( + new UUID(im.fromAgentID), + im.message, + new UUID(im.toAgentID), + im.fromAgentName, + im.dialog, + im.timestamp, + new UUID(im.imSessionID), + im.fromGroup, + im.binaryBucket); + // Message sent + result(true); + return; + } + } + } + + // try child avatar second + foreach (Scene scene in m_Scenes) + { + m_log.DebugFormat("[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID.ToString(), scene.RegionInfo.RegionName); + + if (scene.Entities.ContainsKey(toAgentID) && + scene.Entities[toAgentID] is ScenePresence) + { + // Local message + ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; + + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering to client"); + user.ControllingClient.SendInstantMessage( + new UUID(im.fromAgentID), + im.message, + new UUID(im.toAgentID), + im.fromAgentName, + im.dialog, + im.timestamp, + new UUID(im.imSessionID), + im.fromGroup, + im.binaryBucket); + // Message sent + result(true); + return; + } + } + + if (m_Gridmode) + { + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering via grid"); + // Still here, try send via Grid + SendGridInstantMessageViaXMLRPC(im, result); + return; + } + + m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable"); + result(false); + return; + } + + /// + /// Process a XMLRPC Grid Instant Message + /// + /// XMLRPC parameters + /// + /// Nothing much + protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request) + { + bool successful = false; + // various rational defaults + UUID fromAgentID = UUID.Zero; + UUID fromAgentSession = UUID.Zero; + UUID toAgentID = UUID.Zero; + UUID imSessionID = UUID.Zero; + uint timestamp = 0; + string fromAgentName = ""; + string message = ""; + byte dialog = (byte)0; + bool fromGroup = false; + byte offline = (byte)0; + uint ParentEstateID=0; + Vector3 Position = Vector3.Zero; + UUID RegionID = UUID.Zero ; + byte[] binaryBucket = new byte[0]; + + float pos_x = 0; + float pos_y = 0; + float pos_z = 0; + //m_log.Info("Processing IM"); + + + Hashtable requestData = (Hashtable)request.Params[0]; + // Check if it's got all the data + if (requestData.ContainsKey("from_agent_id") && requestData.ContainsKey("from_agent_session") + && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id") + && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name") + && requestData.ContainsKey("message") && requestData.ContainsKey("dialog") + && requestData.ContainsKey("from_group") + && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id") + && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y") + && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") + && requestData.ContainsKey("binary_bucket")) + { + // Do the easy way of validating the UUIDs + UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); + UUID.TryParse((string)requestData["from_agent_session"], out fromAgentSession); + UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); + UUID.TryParse((string)requestData["im_session_id"], out imSessionID); + UUID.TryParse((string)requestData["region_id"], out RegionID); + + try + { + timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + fromAgentName = (string)requestData["from_agent_name"]; + message = (string)requestData["message"]; + + // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them. + string requestData1 = (string)requestData["dialog"]; + if (string.IsNullOrEmpty(requestData1)) + { + dialog = 0; + } + else + { + byte[] dialogdata = Convert.FromBase64String(requestData1); + dialog = dialogdata[0]; + } + + if ((string)requestData["from_group"] == "TRUE") + fromGroup = true; + + string requestData2 = (string)requestData["offline"]; + if (String.IsNullOrEmpty(requestData2)) + { + offline = 0; + } + else + { + byte[] offlinedata = Convert.FromBase64String(requestData2); + offline = offlinedata[0]; + } + + try + { + ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + try + { + pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + try + { + pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + try + { + pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + Position = new Vector3(pos_x, pos_y, pos_z); + + string requestData3 = (string)requestData["binary_bucket"]; + if (string.IsNullOrEmpty(requestData3)) + { + binaryBucket = new byte[0]; + } + else + { + binaryBucket = Convert.FromBase64String(requestData3); + } + + // Create a New GridInstantMessageObject the the data + GridInstantMessage gim = new GridInstantMessage(); + gim.fromAgentID = fromAgentID.Guid; + gim.fromAgentName = fromAgentName; + gim.fromAgentSession = fromAgentSession.Guid; + gim.fromGroup = fromGroup; + gim.imSessionID = imSessionID.Guid; + gim.RegionID = RegionID.Guid; + gim.timestamp = timestamp; + gim.toAgentID = toAgentID.Guid; + gim.message = message; + gim.dialog = dialog; + gim.offline = offline; + gim.ParentEstateID = ParentEstateID; + gim.Position = Position; + gim.binaryBucket = binaryBucket; + + + // Trigger the Instant message in the scene. + foreach (Scene scene in m_Scenes) + { + if (scene.Entities.ContainsKey(toAgentID) && + scene.Entities[toAgentID] is ScenePresence) + { + ScenePresence user = + (ScenePresence)scene.Entities[toAgentID]; + + if (!user.IsChildAgent) + { + scene.EventManager.TriggerIncomingInstantMessage(gim); + successful = true; + } + } + } + if (!successful) + { + // If the message can't be delivered to an agent, it + // is likely to be a group IM. On a group IM, the + // imSessionID = toAgentID = group id. Raise the + // unhandled IM event to give the groups module + // a chance to pick it up. We raise that in a random + // scene, since the groups module is shared. + // + m_Scenes[0].EventManager.TriggerUnhandledInstantMessage(gim); + } + } + + //Send response back to region calling if it was successful + // calling region uses this to know when to look up a user's location again. + XmlRpcResponse resp = new XmlRpcResponse(); + Hashtable respdata = new Hashtable(); + if (successful) + respdata["success"] = "TRUE"; + else + respdata["success"] = "FALSE"; + resp.Value = respdata; + + return resp; + } + + /// + /// delegate for sending a grid instant message asynchronously + /// + public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result, ulong prevRegionHandle); + + private void GridInstantMessageCompleted(IAsyncResult iar) + { + GridInstantMessageDelegate icon = + (GridInstantMessageDelegate)iar.AsyncState; + icon.EndInvoke(iar); + } + + + protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) + { + GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; + + d.BeginInvoke(im, result, 0, GridInstantMessageCompleted, d); + } + + /// + /// Recursive SendGridInstantMessage over XMLRPC method. + /// + /// + protected virtual void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, ulong prevRegionHandle) + { + UUID toAgentID = new UUID(im.toAgentID); + + UserAgentData upd = null; + + bool lookupAgent = false; + + lock (m_UserRegionMap) + { + if (m_UserRegionMap.ContainsKey(toAgentID)) + { + upd = new UserAgentData(); + upd.AgentOnline = true; + upd.Handle = m_UserRegionMap[toAgentID]; + } + else + { + lookupAgent = true; + } + } + + // Are we needing to look-up an agent? + if (lookupAgent) + { + // Non-cached user agent lookup. + upd = m_Scenes[0].CommsManager.UserService.GetAgentByUUID(toAgentID); + + if (upd != null) + { + // check if we've tried this before.. + // This is one way to end the recursive loop + // + if (upd.Handle == prevRegionHandle) + { + m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); + result(false); + return; + } + } + else + { + m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); + result(false); + return; + } + } + + if (upd != null) + { + if (upd.AgentOnline) + { + RegionInfo reginfo = m_Scenes[0].SceneGridService.RequestNeighbouringRegionInfo(upd.Handle); + if (reginfo != null) + { + Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); + // Not actually used anymore, left in for compatibility + // Remove at next interface change + // + msgdata["region_handle"] = 0; + bool imresult = doIMSending(reginfo, msgdata); + if (imresult) + { + // IM delivery successful, so store the Agent's location in our local cache. + lock (m_UserRegionMap) + { + if (m_UserRegionMap.ContainsKey(toAgentID)) + { + m_UserRegionMap[toAgentID] = upd.Handle; + } + else + { + m_UserRegionMap.Add(toAgentID, upd.Handle); + } + } + result(true); + } + else + { + // try again, but lookup user this time. + // Warning, this must call the Async version + // of this method or we'll be making thousands of threads + // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync + // The version that spawns the thread is SendGridInstantMessageViaXMLRPC + + // This is recursive!!!!! + SendGridInstantMessageViaXMLRPCAsync(im, result, + upd.Handle); + } + + } + } + else + { + result(false); + } + } + else + { + result(false); + } + + } + + /// + /// This actually does the XMLRPC Request + /// + /// RegionInfo we pull the data out of to send the request to + /// The Instant Message data Hashtable + /// Bool if the message was successfully delivered at the other side. + private bool doIMSending(RegionInfo reginfo, Hashtable xmlrpcdata) + { + + ArrayList SendParams = new ArrayList(); + SendParams.Add(xmlrpcdata); + XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); + try + { + + XmlRpcResponse GridResp = GridReq.Send("http://" + reginfo.ExternalHostName + ":" + reginfo.HttpPort, 3000); + + Hashtable responseData = (Hashtable)GridResp.Value; + + if (responseData.ContainsKey("success")) + { + if ((string)responseData["success"] == "TRUE") + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + catch (WebException e) + { + m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to http://{0}:{1} the host didn't respond ({2})", + reginfo.ExternalHostName, reginfo.HttpPort, e.Message); + } + + return false; + } + + /// + /// Get ulong region handle for region by it's Region UUID. + /// We use region handles over grid comms because there's all sorts of free and cool caching. + /// + /// UUID of region to get the region handle for + /// + private ulong getLocalRegionHandleFromUUID(UUID regionID) + { + ulong returnhandle = 0; + + lock (m_Scenes) + { + foreach (Scene sn in m_Scenes) + { + if (sn.RegionInfo.RegionID == regionID) + { + returnhandle = sn.RegionInfo.RegionHandle; + break; + } + } + } + return returnhandle; + } + + /// + /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC + /// + /// The GridInstantMessage object + /// Hashtable containing the XMLRPC request + private Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg) + { + Hashtable gim = new Hashtable(); + gim["from_agent_id"] = msg.fromAgentID.ToString(); + gim["from_agent_session"] = msg.fromAgentSession.ToString(); + gim["to_agent_id"] = msg.toAgentID.ToString(); + gim["im_session_id"] = msg.imSessionID.ToString(); + gim["timestamp"] = msg.timestamp.ToString(); + gim["from_agent_name"] = msg.fromAgentName; + gim["message"] = msg.message; + byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog; + gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None); + + if (msg.fromGroup) + gim["from_group"] = "TRUE"; + else + gim["from_group"] = "FALSE"; + byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline; + gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None); + gim["parent_estate_id"] = msg.ParentEstateID.ToString(); + gim["position_x"] = msg.Position.X.ToString(); + gim["position_y"] = msg.Position.Y.ToString(); + gim["position_z"] = msg.Position.Z.ToString(); + gim["region_id"] = msg.RegionID.ToString(); + gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); + return gim; + } + + } +} diff --git a/OpenSim/Region/Environment/Scenes/EventManager.cs b/OpenSim/Region/Environment/Scenes/EventManager.cs index 5d21dc5f2f..bcefe375f5 100644 --- a/OpenSim/Region/Environment/Scenes/EventManager.cs +++ b/OpenSim/Region/Environment/Scenes/EventManager.cs @@ -144,9 +144,11 @@ namespace OpenSim.Region.Environment.Scenes public event SignificantClientMovement OnSignificantClientMovement; - public delegate void NewGridInstantMessage(GridInstantMessage message, InstantMessageReceiver whichModule); + public delegate void IncomingInstantMessage(GridInstantMessage message); - public event NewGridInstantMessage OnGridInstantMessage; + public event IncomingInstantMessage OnIncomingInstantMessage; + + public event IncomingInstantMessage OnUnhandledInstantMessage; public delegate void ClientClosed(UUID clientID); @@ -352,7 +354,8 @@ namespace OpenSim.Region.Environment.Scenes private LandObjectAdded handlerLandObjectAdded = null; //OnLandObjectAdded; private LandObjectRemoved handlerLandObjectRemoved = null; //OnLandObjectRemoved; private AvatarEnteringNewParcel handlerAvatarEnteringNewParcel = null; //OnAvatarEnteringNewParcel; - private NewGridInstantMessage handlerGridInstantMessage = null; //OnGridInstantMessage; + private IncomingInstantMessage handlerIncomingInstantMessage = null; //OnIncomingInstantMessage; + private IncomingInstantMessage handlerUnhandledInstantMessage = null; //OnUnhandledInstantMessage; private ClientClosed handlerClientClosed = null; //OnClientClosed; private OnMakeChildAgentDelegate handlerMakeChildAgent = null; //OnMakeChildAgent; private OnMakeRootAgentDelegate handlerMakeRootAgent = null; //OnMakeRootAgent; @@ -641,15 +644,21 @@ namespace OpenSim.Region.Environment.Scenes } } - ///Used to pass instnat messages around between the Scene, the Friends Module and the Instant Messsage Module - ///Object containing the Instant Message Data - ///A bit vector containing the modules to send the message to - public void TriggerGridInstantMessage(GridInstantMessage message, InstantMessageReceiver whichModule) + public void TriggerIncomingInstantMessage(GridInstantMessage message) { - handlerGridInstantMessage = OnGridInstantMessage; - if (handlerGridInstantMessage != null) + handlerIncomingInstantMessage = OnIncomingInstantMessage; + if (handlerIncomingInstantMessage != null) { - handlerGridInstantMessage(message, whichModule); + handlerIncomingInstantMessage(message); + } + } + + public void TriggerUnhandledInstantMessage(GridInstantMessage message) + { + handlerUnhandledInstantMessage = OnUnhandledInstantMessage; + if (handlerUnhandledInstantMessage != null) + { + handlerUnhandledInstantMessage(message); } } diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index 08b7bdb377..7485134590 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -993,7 +993,8 @@ namespace OpenSim.Region.Environment.Scenes else msg.message = string.Format("Your object {0} was returned from {1} in region {2} due to parcel auto return", ret.Value.objectName, ret.Value.location.ToString(), RegionInfo.RegionName); - TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule); +// TODO: Send IM +// TriggerGridInstantMessage(msg); } } @@ -3304,21 +3305,6 @@ namespace OpenSim.Region.Environment.Scenes return UUID.Zero; } - /// - /// This method is a way for the Friends Module to create an instant - /// message to the avatar and for Instant Messages that travel across - /// gridcomms to make it to the Instant Message Module. - /// - /// Friendship establishment and groups are unfortunately tied with instant messaging and - /// there's no way to separate them completely. - /// - /// object containing the instant message data - /// void - public void TriggerGridInstantMessage(GridInstantMessage message, InstantMessageReceiver options) - { - m_eventManager.TriggerGridInstantMessage(message, options); - } - public virtual void StoreAddFriendship(UUID ownerID, UUID friendID, uint perms) { // TODO: m_sceneGridService.DoStuff; diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs index d929be228d..acb6888c30 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs @@ -1211,6 +1211,9 @@ namespace OpenSim.Region.Environment.Scenes /// public void ProcessBackup(IRegionDataStore datastore) { + if (!m_isBackedUp) + return; + // Since this is the top of the section of call stack for backing up a particular scene object, don't let // any exception propogate upwards. diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 0720f363fc..cce7e7ab21 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -2645,7 +2645,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api msg.Position = Vector3.Zero;// new Vector3(m_host.AbsolutePosition); msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid; msg.binaryBucket = new byte[0];// binaryBucket; - World.TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule); +// TODO: Send IM +// World.TriggerGridInstantMessage(msg); // ScriptSleep(2000); }