From 5e4d6cab00cb29cd088ab7b62ab13aff103b64cb Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 19 May 2019 21:24:15 +1000 Subject: Dump OpenSim 0.9.0.1 into it's own branch. --- .../InstantMessage/HGMessageTransferModule.cs | 31 +-- .../Avatar/InstantMessage/InstantMessageModule.cs | 69 +++-- .../Avatar/InstantMessage/MessageTransferModule.cs | 297 ++++++++++++++------- .../Avatar/InstantMessage/MuteListModule.cs | 4 +- .../Avatar/InstantMessage/MuteListModuleTst.cs | 229 ++++++++++++++++ .../Avatar/InstantMessage/OfflineMessageModule.cs | 132 ++++++--- .../Avatar/InstantMessage/XMuteModule.cs | 239 +++++++++++++++++ 7 files changed, 824 insertions(+), 177 deletions(-) create mode 100644 OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModuleTst.cs create mode 100644 OpenSim/Region/CoreModules/Avatar/InstantMessage/XMuteModule.cs (limited to 'OpenSim/Region/CoreModules/Avatar/InstantMessage') diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs index a1b918a..16e2ec3 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs @@ -144,11 +144,11 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage foreach (Scene scene in m_Scenes) { // m_log.DebugFormat( -// "[HG INSTANT MESSAGE]: Looking for root agent {0} in {1}", +// "[HG INSTANT MESSAGE]: Looking for root agent {0} in {1}", // toAgentID.ToString(), scene.RegionInfo.RegionName); - ScenePresence sp = scene.GetScenePresence(toAgentID); - if (sp != null && !sp.IsChildAgent) - { + ScenePresence sp = scene.GetScenePresence(toAgentID); + if (sp != null && !sp.IsChildAgent && !sp.IsDeleted) + { // Local message // m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", user.Name, toAgentID); sp.ControllingClient.SendInstantMessage(im); @@ -159,25 +159,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } - // try child avatar second - foreach (Scene scene in m_Scenes) - { -// m_log.DebugFormat( -// "[HG INSTANT MESSAGE]: Looking for child of {0} in {1}", -// toAgentID, scene.RegionInfo.RegionName); - ScenePresence sp = scene.GetScenePresence(toAgentID); - if (sp != null) - { - // Local message -// m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", user.Name, toAgentID); - sp.ControllingClient.SendInstantMessage(im); - - // Message sent - result(true); - return; - } - } - // m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); // Is the user a local user? string url = string.Empty; @@ -224,7 +205,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage foreach (Scene scene in m_Scenes) { ScenePresence sp = scene.GetScenePresence(toAgentID); - if(sp != null && !sp.IsChildAgent) + if(sp != null && !sp.IsChildAgent && !sp.IsDeleted) { scene.EventManager.TriggerIncomingInstantMessage(gim); successful = true; @@ -310,7 +291,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage foreach (Scene scene in m_Scenes) { ScenePresence presence = scene.GetScenePresence(agentID); - if (presence != null && !presence.IsChildAgent) + if (presence != null && !presence.IsChildAgent && !presence.IsDeleted) return presence.ControllingClient; } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs index c33a296..71c0a8a 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Timers; using log4net; using Mono.Addins; using Nini.Config; @@ -47,15 +48,15 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage /// /// Is this module enabled? /// - private bool m_enabled = false; - - private readonly List m_scenes = new List(); + protected bool m_enabled = false; + + protected readonly List m_scenes = new List(); #region Region Module interface - private IMessageTransferModule m_TransferModule = null; + protected IMessageTransferModule m_TransferModule = null; - public void Initialise(IConfigSource config) + public virtual void Initialise(IConfigSource config) { if (config.Configs["Messaging"] != null) { @@ -64,11 +65,11 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage "InstantMessageModule") return; } - + m_enabled = true; } - public void AddRegion(Scene scene) + public virtual void AddRegion(Scene scene) { if (!m_enabled) return; @@ -84,7 +85,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } - public void RegionLoaded(Scene scene) + public virtual void RegionLoaded(Scene scene) { if (!m_enabled) return; @@ -106,7 +107,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } - public void RemoveRegion(Scene scene) + public virtual void RemoveRegion(Scene scene) { if (!m_enabled) return; @@ -117,7 +118,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } - void OnClientConnect(IClientCore client) + protected virtual void OnClientConnect(IClientCore client) { IClientIM clientIM; if (client.TryGet(out clientIM)) @@ -126,27 +127,33 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } - public void PostInitialise() + public virtual void PostInitialise() { } - public void Close() + public virtual void Close() { } - public string Name + public virtual string Name { get { return "InstantMessageModule"; } } - public Type ReplaceableInterface + public virtual Type ReplaceableInterface { get { return null; } } #endregion - - public void OnInstantMessage(IClientAPI client, GridInstantMessage im) +/* + public virtual void OnViewerInstantMessage(IClientAPI client, GridInstantMessage im) + { + im.fromAgentName = client.FirstName + " " + client.LastName; + OnInstantMessage(client, im); + } +*/ + public virtual void OnInstantMessage(IClientAPI client, GridInstantMessage im) { byte dialog = im.dialog; @@ -159,10 +166,34 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage return; } + //DateTime dt = DateTime.UtcNow; + + // Ticks from UtcNow, but make it look like local. Evil, huh? + //dt = DateTime.SpecifyKind(dt, DateTimeKind.Local); + + //try + //{ + // // Convert that to the PST timezone + // TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"); + // dt = TimeZoneInfo.ConvertTime(dt, timeZoneInfo); + //} + //catch + //{ + // //m_log.Info("[OFFLINE MESSAGING]: No PST timezone found on this machine. Saving with local timestamp."); + //} + + //// And make it look local again to fool the unix time util + //dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); + + // If client is null, this message comes from storage and IS offline + if (client != null) + im.offline = 0; + + if (im.offline == 0) + im.timestamp = (uint)Util.UnixTimeSinceEpoch(); + if (m_TransferModule != null) { - if (client != null) - im.fromAgentName = client.FirstName + " " + client.LastName; m_TransferModule.SendInstantMessage(im, delegate(bool success) { @@ -193,7 +224,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage /// /// /// - private void OnGridInstantMessage(GridInstantMessage msg) + protected virtual void OnGridInstantMessage(GridInstantMessage msg) { // Just call the Text IM handler above // This event won't be raised unless we have that agent, diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index 2462ff8..efb9421 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs @@ -50,6 +50,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool m_Enabled = false; + protected string m_MessageKey = String.Empty; protected List m_Scenes = new List(); protected Dictionary m_UserRegionMap = new Dictionary(); @@ -69,14 +70,17 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage public virtual void Initialise(IConfigSource config) { IConfig cnf = config.Configs["Messaging"]; - if (cnf != null && cnf.GetString( - "MessageTransferModule", "MessageTransferModule") != - "MessageTransferModule") + if (cnf != null) { - m_log.Debug("[MESSAGE TRANSFER]: Disabled by configuration"); - return; - } + if (cnf.GetString("MessageTransferModule", + "MessageTransferModule") != "MessageTransferModule") + { + return; + } + m_MessageKey = cnf.GetString("MessageKey", String.Empty); + } + m_log.Debug("[MESSAGE TRANSFER]: Module enabled"); m_Enabled = true; } @@ -135,53 +139,45 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { UUID toAgentID = new UUID(im.toAgentID); + if (toAgentID == UUID.Zero) + return; + + IClientAPI client = null; + // Try root avatar only first foreach (Scene scene in m_Scenes) { -// m_log.DebugFormat( -// "[INSTANT MESSAGE]: Looking for root agent {0} in {1}", -// toAgentID.ToString(), scene.RegionInfo.RegionName); - ScenePresence sp = scene.GetScenePresence(toAgentID); - if (sp != null && !sp.IsChildAgent) + if (sp != null && !sp.IsDeleted && sp.ControllingClient.IsActive) { - // Local message -// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); - - sp.ControllingClient.SendInstantMessage(im); + // actualy don't send via child agents + // ims can be complex things, and not sure viewers will not mess up + if(sp.IsChildAgent) + continue; - // Message sent - result(true); - return; + client = sp.ControllingClient; + if(!sp.IsChildAgent) + break; } } - // try child avatar second - foreach (Scene scene in m_Scenes) + if(client != null) { -// m_log.DebugFormat( -// "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); - - ScenePresence sp = scene.GetScenePresence(toAgentID); - if (sp != null) - { - // Local message -// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID); + // Local message +// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); - sp.ControllingClient.SendInstantMessage(im); + client.SendInstantMessage(im); // Message sent - result(true); - return; - } + result(true); + return; } - // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); SendGridInstantMessageViaXMLRPC(im, result); } - public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) + public virtual void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) { UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; @@ -211,10 +207,10 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient) { bool successful = false; - + // TODO: For now, as IMs seem to be a bit unreliable on OSGrid, catch all exception that // happen here and aren't caught and log them. - try + try { // various rational defaults UUID fromAgentID = UUID.Zero; @@ -236,7 +232,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage 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") @@ -249,6 +244,19 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") && requestData.ContainsKey("binary_bucket")) { + if (m_MessageKey != String.Empty) + { + XmlRpcResponse error_resp = new XmlRpcResponse(); + Hashtable error_respdata = new Hashtable(); + error_respdata["success"] = "FALSE"; + error_resp.Value = error_respdata; + + if (!requestData.Contains("message_key")) + return error_resp; + if (m_MessageKey != (string)requestData["message_key"]) + return error_resp; + } + // Do the easy way of validating the UUIDs UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); @@ -382,7 +390,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage gim.Position = Position; gim.binaryBucket = binaryBucket; - // Trigger the Instant message in the scene. foreach (Scene scene in m_Scenes) { @@ -425,104 +432,191 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage return resp; } + /// /// delegate for sending a grid instant message asynchronously /// - public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result); + private delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result); + + private class GIM { + public GridInstantMessage im; + public MessageResultNotification result; + }; - protected virtual void GridInstantMessageCompleted(IAsyncResult iar) + private Queue pendingInstantMessages = new Queue(); + private int numInstantMessageThreads = 0; + + private void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) { - GridInstantMessageDelegate icon = - (GridInstantMessageDelegate)iar.AsyncState; - icon.EndInvoke(iar); + lock (pendingInstantMessages) { + if (numInstantMessageThreads >= 4) { + GIM gim = new GIM(); + gim.im = im; + gim.result = result; + pendingInstantMessages.Enqueue(gim); + } else { + ++ numInstantMessageThreads; + //m_log.DebugFormat("[SendGridInstantMessageViaXMLRPC]: ++numInstantMessageThreads={0}", numInstantMessageThreads); + GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsyncMain; + d.BeginInvoke(im, result, GridInstantMessageCompleted, d); + } + } } - protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) + private void GridInstantMessageCompleted(IAsyncResult iar) { - GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; - - d.BeginInvoke(im, result, GridInstantMessageCompleted, d); + GridInstantMessageDelegate d = (GridInstantMessageDelegate)iar.AsyncState; + d.EndInvoke(iar); } /// /// Internal SendGridInstantMessage over XMLRPC method. /// - /// - /// This is called from within a dedicated thread. - /// - private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result) + + /// + /// Pass in 0 the first time this method is called. It will be called recursively with the last + /// regionhandle tried + /// + private void SendGridInstantMessageViaXMLRPCAsyncMain(GridInstantMessage im, MessageResultNotification result) + { + GIM gim; + do { + try { + SendGridInstantMessageViaXMLRPCAsync(im, result, UUID.Zero); + } catch (Exception e) { + m_log.Error("[SendGridInstantMessageViaXMLRPC]: exception " + e.Message); + } + lock (pendingInstantMessages) { + if (pendingInstantMessages.Count > 0) { + gim = pendingInstantMessages.Dequeue(); + im = gim.im; + result = gim.result; + } else { + gim = null; + -- numInstantMessageThreads; + //m_log.DebugFormat("[SendGridInstantMessageViaXMLRPC]: --numInstantMessageThreads={0}", numInstantMessageThreads); + } + } + } while (gim != null); + } + + private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, UUID prevRegionID) { + UUID toAgentID = new UUID(im.toAgentID); - UUID regionID; - bool needToLookupAgent; + PresenceInfo upd = null; + bool lookupAgent = false; lock (m_UserRegionMap) - needToLookupAgent = !m_UserRegionMap.TryGetValue(toAgentID, out regionID); - - while (true) { - if (needToLookupAgent) + if (m_UserRegionMap.ContainsKey(toAgentID)) { - PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); - - UUID foundRegionID = UUID.Zero; + upd = new PresenceInfo(); + upd.RegionID = m_UserRegionMap[toAgentID]; - if (presences != null) + // We need to compare the current regionhandle with the previous region handle + // or the recursive loop will never end because it will never try to lookup the agent again + if (prevRegionID == upd.RegionID) { - foreach (PresenceInfo p in presences) - { - if (p.RegionID != UUID.Zero) - { - foundRegionID = p.RegionID; - break; - } - } + lookupAgent = true; } - - // If not found or the found region is the same as the last lookup, then message is undeliverable - if (foundRegionID == UUID.Zero || foundRegionID == regionID) - break; - else - regionID = foundRegionID; } - - GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, regionID); - if (reginfo == null) + else { - m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", regionID); - break; + lookupAgent = true; } + } - // Try to send the message to the agent via the retrieved region. - Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); - msgdata["region_handle"] = 0; - bool imresult = doIMSending(reginfo, msgdata); - // If the message delivery was successful, then cache the entry. - if (imresult) + // Are we needing to look-up an agent? + if (lookupAgent) + { + // Non-cached user agent lookup. + PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); + if (presences != null && presences.Length > 0) { - lock (m_UserRegionMap) + foreach (PresenceInfo p in presences) { - m_UserRegionMap[toAgentID] = regionID; + if (p.RegionID != UUID.Zero) + { + upd = p; + break; + } } - result(true); - return; } - // If we reach this point in the first iteration of the while, then we may have unsuccessfully tried - // to use a locally cached region ID. All subsequent attempts need to lookup agent details from - // the presence service. - needToLookupAgent = true; + if (upd != null) + { + // check if we've tried this before.. + // This is one way to end the recursive loop + // + if (upd.RegionID == prevRegionID) + { + // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); + HandleUndeliverableMessage(im, result); + return; + } + } + else + { + // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); + HandleUndeliverableMessage(im, result); + return; + } } - // If we reached this point then the message was not deliverable. Remove the bad cache entry and - // signal the delivery failure. - lock (m_UserRegionMap) - m_UserRegionMap.Remove(toAgentID); + if (upd != null) + { + GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(UUID.Zero, + upd.RegionID); + 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.RegionID; + } + else + { + m_UserRegionMap.Add(toAgentID, upd.RegionID); + } + } + 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 - // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); - HandleUndeliverableMessage(im, result); + // This is recursive!!!!! + SendGridInstantMessageViaXMLRPCAsync(im, result, + upd.RegionID); + } + } + else + { + m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID); + HandleUndeliverableMessage(im, result); + } + } + else + { + HandleUndeliverableMessage(im, result); + } } /// @@ -622,8 +716,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage gim["position_z"] = msg.Position.Z.ToString(); gim["region_id"] = new UUID(msg.RegionID).ToString(); gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); + if (m_MessageKey != String.Empty) + gim["message_key"] = m_MessageKey; return gim; } - } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs index 315d372..2d57193 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs @@ -118,11 +118,11 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { get { return null; } } - + public void Close() { } - + private void OnNewClient(IClientAPI client) { client.OnMuteListRequest += OnMuteListRequest; diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModuleTst.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModuleTst.cs new file mode 100644 index 0000000..6857f35 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModuleTst.cs @@ -0,0 +1,229 @@ +/* + * 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.Text; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Mono.Addins; + +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Region.CoreModules.Avatar.InstantMessage +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MuteListModuleTst")] + public class MuteListModuleTst : ISharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected bool m_Enabled = false; + protected List m_SceneList = new List(); + protected IMuteListService m_service = null; + + public void Initialise(IConfigSource config) + { + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null) + return; + + if (cnf.GetString("MuteListModule", "None") != "MuteListModuleTst") + return; + + m_Enabled = true; + } + + public void AddRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + IXfer xfer = scene.RequestModuleInterface(); + if (xfer == null) + { + m_log.ErrorFormat("[MuteListModuleTst]: Xfer not availble in region {0}. Module Disabled", scene.Name); + m_Enabled = false; + return; + } + + IMuteListService srv = scene.RequestModuleInterface(); + if(srv == null) + { + m_log.ErrorFormat("[MuteListModuleTst]: MuteListService not availble in region {0}. Module Disabled", scene.Name); + m_Enabled = false; + return; + } + lock (m_SceneList) + { + if(m_service == null) + m_service = srv; + m_SceneList.Add(scene); + scene.EventManager.OnNewClient += OnNewClient; + } + } + + public void RemoveRegion(Scene scene) + { + lock (m_SceneList) + { + if(m_SceneList.Contains(scene)) + { + m_SceneList.Remove(scene); + scene.EventManager.OnNewClient -= OnNewClient; + } + } + } + + public void PostInitialise() + { + if (!m_Enabled) + return; + + m_log.Debug("[MuteListModuleTst]: enabled"); + } + + public string Name + { + get { return "MuteListModuleTst"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void Close() + { + } + + private void OnNewClient(IClientAPI client) + { + client.OnMuteListRequest += OnMuteListRequest; + client.OnUpdateMuteListEntry += OnUpdateMuteListEntry; + client.OnRemoveMuteListEntry += OnRemoveMuteListEntry; + } + + private void OnMuteListRequest(IClientAPI client, uint crc) + { + if (!m_Enabled) + { + if(crc == 0) + client.SendEmpytMuteList(); + else + client.SendUseCachedMuteList(); + return; + } + + IXfer xfer = client.Scene.RequestModuleInterface(); + if (xfer == null) + { + if(crc == 0) + client.SendEmpytMuteList(); + else + client.SendUseCachedMuteList(); + return; + } + + Byte[] data = m_service.MuteListRequest(client.AgentId, crc); + if (data == null) + { + if(crc == 0) + client.SendEmpytMuteList(); + else + client.SendUseCachedMuteList(); + return; + } + + if (data.Length == 0) + { + client.SendEmpytMuteList(); + return; + } + + if (data.Length == 1) + { + if(crc == 0) + client.SendEmpytMuteList(); + else + client.SendUseCachedMuteList(); + return; + } + + string filename = "mutes" + client.AgentId.ToString(); + xfer.AddNewFile(filename, data); + client.SendMuteListUpdate(filename); + } + + private void OnUpdateMuteListEntry(IClientAPI client, UUID muteID, string muteName, int muteType, uint muteFlags) + { + if (!m_Enabled) + return; + + UUID agentID = client.AgentId; + if(muteType == 1) // agent + { + if(agentID == muteID) + return; + if(m_SceneList[0].Permissions.IsAdministrator(muteID)) + { + OnMuteListRequest(client, 0); + return; + } + } + + MuteData mute = new MuteData(); + mute.AgentID = agentID; + mute.MuteID = muteID; + mute.MuteName = muteName; + mute.MuteType = muteType; + mute.MuteFlags = (int)muteFlags; + mute.Stamp = Util.UnixTimeSinceEpoch(); + + m_service.UpdateMute(mute); + } + + private void OnRemoveMuteListEntry(IClientAPI client, UUID muteID, string muteName) + { + if (!m_Enabled) + return; + m_service.RemoveMute(client.AgentId, muteID, muteName); + } + } +} + diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs index 9cdb1c2..315ce1b 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs @@ -39,16 +39,25 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { + public struct SendReply + { + public bool Success; + public string Message; + public int Disposition; + } + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OfflineMessageModule")] public class OfflineMessageModule : ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool enabled = true; + private bool m_UseNewAvnCode = false; private List m_SceneList = new List(); private string m_RestURL = String.Empty; IMessageTransferModule m_TransferModule = null; private bool m_ForwardOfflineGroupMessages = true; + private Dictionary> m_repliesSent= new Dictionary>(); public void Initialise(IConfigSource config) { @@ -74,6 +83,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", m_ForwardOfflineGroupMessages); + m_UseNewAvnCode = cnf.GetBoolean("UseNewAvnCode", m_UseNewAvnCode); } public void AddRegion(Scene scene) @@ -138,7 +148,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { get { return null; } } - + public void Close() { } @@ -168,11 +178,21 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage private void OnNewClient(IClientAPI client) { client.OnRetrieveInstantMessages += RetrieveInstantMessages; + client.OnLogout += OnClientLoggedOut; + } + + public void OnClientLoggedOut(IClientAPI client) + { + m_repliesSent.Remove(client); } private void RetrieveInstantMessages(IClientAPI client) { - if (m_RestURL != "") + if (m_RestURL == String.Empty) + { + return; + } + else { m_log.DebugFormat("[OFFLINE MESSAGING]: Retrieving stored messages for {0}", client.AgentId); @@ -180,28 +200,28 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage = SynchronousRestObjectRequester.MakeRequest>( "POST", m_RestURL + "/RetrieveMessages/", client.AgentId); - if (msglist == null) - { - m_log.WarnFormat("[OFFLINE MESSAGING]: WARNING null message list."); - return; - } - - foreach (GridInstantMessage im in msglist) + if (msglist != null) { - if (im.dialog == (byte)InstantMessageDialog.InventoryOffered) - // send it directly or else the item will be given twice - client.SendInstantMessage(im); - else + foreach (GridInstantMessage im in msglist) { - // Send through scene event manager so all modules get a chance - // to look at this message before it gets delivered. - // - // Needed for proper state management for stored group - // invitations - // - Scene s = FindScene(client.AgentId); - if (s != null) - s.EventManager.TriggerIncomingInstantMessage(im); + if (im.dialog == (byte)InstantMessageDialog.InventoryOffered) + // send it directly or else the item will be given twice + client.SendInstantMessage(im); + else + { + // Send through scene event manager so all modules get a chance + // to look at this message before it gets delivered. + // + // Needed for proper state management for stored group + // invitations + // + + im.offline = 1; + + Scene s = FindScene(client.AgentId); + if (s != null) + s.EventManager.TriggerIncomingInstantMessage(im); + } } } } @@ -213,7 +233,8 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage im.dialog != (byte)InstantMessageDialog.MessageFromAgent && im.dialog != (byte)InstantMessageDialog.GroupNotice && im.dialog != (byte)InstantMessageDialog.GroupInvitation && - im.dialog != (byte)InstantMessageDialog.InventoryOffered) + im.dialog != (byte)InstantMessageDialog.InventoryOffered && + im.dialog != (byte)InstantMessageDialog.TaskInventoryOffered) { return; } @@ -225,22 +246,73 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage return; } - bool success = SynchronousRestObjectRequester.MakeRequest( - "POST", m_RestURL+"/SaveMessage/", im, 10000); + if(m_UseNewAvnCode) + { + Scene scene = FindScene(new UUID(im.fromAgentID)); + if (scene == null) + scene = m_SceneList[0]; - if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) + UUID scopeID = scene.RegionInfo.ScopeID; + SendReply reply = SynchronousRestObjectRequester.MakeRequest( + "POST", m_RestURL+"/SaveMessage/?scope=" + scopeID.ToString(), im, 20000); + + if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) + { + IClientAPI client = FindClient(new UUID(im.fromAgentID)); + if (client == null) + return; + + if (string.IsNullOrEmpty(reply.Message)) + reply.Message = "User is not logged in. " + (reply.Success ? "Message saved." : "Message not saved"); + + bool sendReply = true; + + switch (reply.Disposition) + { + case 0: // Normal + break; + case 1: // Only once per user + if (m_repliesSent.ContainsKey(client) && m_repliesSent[client].Contains(new UUID(im.toAgentID))) + sendReply = false; + else + { + if (!m_repliesSent.ContainsKey(client)) + m_repliesSent[client] = new List(); + m_repliesSent[client].Add(new UUID(im.toAgentID)); + } + break; + } + + if (sendReply) + { + client.SendInstantMessage(new GridInstantMessage( + null, new UUID(im.toAgentID), + "System", new UUID(im.fromAgentID), + (byte)InstantMessageDialog.MessageFromAgent, + reply.Message, + false, new Vector3())); + } + } + } + else { - IClientAPI client = FindClient(new UUID(im.fromAgentID)); - if (client == null) - return; + bool success = SynchronousRestObjectRequester.MakeRequest( + "POST", m_RestURL+"/SaveMessage/", im, 20000); + + if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) + { + IClientAPI client = FindClient(new UUID(im.fromAgentID)); + if (client == null) + return; - client.SendInstantMessage(new GridInstantMessage( + client.SendInstantMessage(new GridInstantMessage( null, new UUID(im.toAgentID), "System", new UUID(im.fromAgentID), (byte)InstantMessageDialog.MessageFromAgent, "User is not logged in. "+ (success ? "Message saved." : "Message not saved"), false, new Vector3())); + } } } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/XMuteModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/XMuteModule.cs new file mode 100644 index 0000000..b61e848 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/XMuteModule.cs @@ -0,0 +1,239 @@ +/* + * 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.Text; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Mono.Addins; +using OpenSim.Data.MySQL; +using MySql.Data.MySqlClient; + + +namespace OpenSim.Region.CoreModules.Avatar.InstantMessage +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XMute")] + public class XMuteModule : ISharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected bool m_Enabled = true; + protected List m_SceneList = new List(); + protected MuteTableHandler m_MuteTable; + protected string m_DatabaseConnect; + + public void Initialise(IConfigSource config) + { + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null) + { + m_Enabled = false; + return; + } + + if (cnf.GetString("MuteListModule", "None") != + "XMute") + { + m_Enabled = false; + return; + } + + m_DatabaseConnect = cnf.GetString("MuteDatabaseConnect", String.Empty); + if (m_DatabaseConnect == String.Empty) + { + m_log.Debug("[XMute]: MuteDatabaseConnect missing or empty"); + m_Enabled = false; + return; + } + + m_MuteTable = new MuteTableHandler( + m_DatabaseConnect, "XMute", String.Empty); + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + lock (m_SceneList) + { + m_SceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + } + } + + public void RegionLoaded(Scene scene) + { + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + lock (m_SceneList) + { + m_SceneList.Remove(scene); + } + } + + public void PostInitialise() + { + if (!m_Enabled) + return; + + m_log.Debug("[XMute]: Mute list enabled"); + } + + public string Name + { + get { return "XMuteModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void Close() + { + } + + private void OnNewClient(IClientAPI client) + { + client.OnMuteListRequest += OnMuteListRequest; + client.OnUpdateMuteListEntry += OnUpdateMuteListEntry; + client.OnRemoveMuteListEntry += OnRemoveMuteListEntry; + } + + private void OnMuteListRequest(IClientAPI client, uint crc) + { + string filename = "mutes"+client.AgentId.ToString(); + + IXfer xfer = client.Scene.RequestModuleInterface(); + if (xfer != null) + { + MuteData[] data = m_MuteTable.Get("AgentID", client.AgentId.ToString()); + if (data == null || data.Length == 0) + { + xfer.AddNewFile(filename, new Byte[0]); + } + else + { + StringBuilder sb = new StringBuilder(1024); + + foreach (MuteData d in data) + sb.AppendFormat("{0} {1} {2}|{3}\n", + d.MuteType, + d.MuteID.ToString(), + d.MuteName, + d.MuteFlags); + + Byte[] filedata = Util.UTF8.GetBytes(sb.ToString()); + + uint dataCrc = Crc32.Compute(filedata); + + if (dataCrc == crc) + { + client.SendUseCachedMuteList(); + return; + } + + xfer.AddNewFile(filename, filedata); + } + + client.SendMuteListUpdate(filename); + } + } + + private void OnUpdateMuteListEntry(IClientAPI client, UUID muteID, string muteName, int muteType, uint muteFlags) + { + MuteData mute = new MuteData(); + + mute.AgentID = client.AgentId; + mute.MuteID = muteID; + mute.MuteName = muteName; + mute.MuteType = muteType; + mute.MuteFlags = (int)muteFlags; + mute.Stamp = Util.UnixTimeSinceEpoch(); + + m_MuteTable.Store(mute); + } + + private void OnRemoveMuteListEntry(IClientAPI client, UUID muteID, string muteName) + { + m_MuteTable.Delete(new string[] { "AgentID", + "MuteID", + "MuteName" }, + new string[] { client.AgentId.ToString(), + muteID.ToString(), + muteName }); + } + } + + public class MuteTableHandler : MySQLGenericTableHandler + { + public MuteTableHandler(string conn, string realm, string m) : base(conn, realm, m) + { + } + + public bool Delete(string[] fields, string[] val) + { + if (fields.Length != val.Length) + return false; + + using (MySqlCommand cmd = new MySqlCommand()) + { + string text = String.Format("delete from {0} where ", m_Realm); + + List terms = new List(); + + for (int i = 0 ; i < fields.Length ; i++) + { + terms.Add(String.Format("{0} = ?{0}", fields[i])); + cmd.Parameters.AddWithValue("?" + fields[i], val[i]); + } + + text += string.Join(" and ", terms.ToArray()); + + cmd.CommandText = text; + + if (ExecuteNonQuery(cmd) > 0) + return true; + return false; + } + } + } +} + -- cgit v1.1