From e977761071a2d614a9a621437fbf86479b414759 Mon Sep 17 00:00:00 2001 From: SignpostMarv Date: Tue, 23 Oct 2012 15:42:16 +0100 Subject: adding ability for listeners to be filtered by regular expressions and a general-purpose function to see if a given string matches a given regex --- .../Scripting/WorldComm/WorldCommModule.cs | 129 ++++++++++++++++++--- OpenSim/Region/Framework/Interfaces/IWorldComm.cs | 25 ++++ .../Shared/Api/Implementation/OSSL_Api.cs | 63 ++++++++++ .../ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs | 24 ++++ .../Shared/Api/Runtime/LSL_Constants.cs | 10 ++ .../ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs | 10 ++ 6 files changed, 242 insertions(+), 19 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs index c68ed6b..cf0eb2a 100644 --- a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs @@ -28,6 +28,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; @@ -170,12 +171,42 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm /// UUID of the SceneObjectPart /// channel to listen on /// name to filter on - /// key to filter on (user given, could be totally faked) + /// + /// key to filter on (user given, could be totally faked) + /// /// msg to filter on /// number of the scripts handle - public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) + public int Listen(uint localID, UUID itemID, UUID hostID, int channel, + string name, UUID id, string msg) { - return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); + return m_listenerManager.AddListener(localID, itemID, hostID, + channel, name, id, msg); + } + + /// + /// Create a listen event callback with the specified filters. + /// The parameters localID,itemID are needed to uniquely identify + /// the script during 'peek' time. Parameter hostID is needed to + /// determine the position of the script. + /// + /// localID of the script engine + /// UUID of the script engine + /// UUID of the SceneObjectPart + /// channel to listen on + /// name to filter on + /// + /// key to filter on (user given, could be totally faked) + /// + /// msg to filter on + /// + /// Bitfield indicating which strings should be processed as regex. + /// + /// number of the scripts handle + public int Listen(uint localID, UUID itemID, UUID hostID, int channel, + string name, UUID id, string msg, int regexBitfield) + { + return m_listenerManager.AddListener(localID, itemID, hostID, + channel, name, id, msg, regexBitfield); } /// @@ -465,10 +496,20 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_curlisteners = 0; } - public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) + public int AddListener(uint localID, UUID itemID, UUID hostID, + int channel, string name, UUID id, string msg) + { + return AddListener(localID, itemID, hostID, channel, name, id, + msg, 0); + } + + public int AddListener(uint localID, UUID itemID, UUID hostID, + int channel, string name, UUID id, string msg, + int regexBitfield) { // do we already have a match on this particular filter event? - List coll = GetListeners(itemID, channel, name, id, msg); + List coll = GetListeners(itemID, channel, name, id, + msg); if (coll.Count > 0) { @@ -485,7 +526,9 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm if (newHandle > 0) { - ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg); + ListenerInfo li = new ListenerInfo(newHandle, localID, + itemID, hostID, channel, name, id, msg, + regexBitfield); List listeners; if (!m_listeners.TryGetValue(channel,out listeners)) @@ -626,6 +669,22 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm return -1; } + /// These are duplicated from ScriptBaseClass + /// http://opensimulator.org/mantis/view.php?id=6106#c21945 + #region Constants for the bitfield parameter of osListenRegex + + /// + /// process name parameter as regex + /// + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// + /// process message parameter as regex + /// + public const int OS_LISTEN_REGEX_MESSAGE = 0x2; + + #endregion + // Theres probably a more clever and efficient way to // do this, maybe with regex. // PM2008: Ha, one could even be smart and define a specialized Enumerator. @@ -651,7 +710,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { continue; } - if (li.GetName().Length > 0 && !li.GetName().Equals(name)) + if (li.GetName().Length > 0 && ( + ((li.GetRegexBitfield() & OS_LISTEN_REGEX_NAME) != OS_LISTEN_REGEX_NAME && !li.GetName().Equals(name)) || + ((li.GetRegexBitfield() & OS_LISTEN_REGEX_NAME) == OS_LISTEN_REGEX_NAME && !Regex.IsMatch(name, li.GetName())) + )) { continue; } @@ -659,7 +721,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { continue; } - if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg)) + if (li.GetMessage().Length > 0 && ( + ((li.GetRegexBitfield() & OS_LISTEN_REGEX_MESSAGE) != OS_LISTEN_REGEX_MESSAGE && !li.GetMessage().Equals(msg)) || + ((li.GetRegexBitfield() & OS_LISTEN_REGEX_MESSAGE) == OS_LISTEN_REGEX_MESSAGE && !Regex.IsMatch(msg, li.GetMessage())) + )) { continue; } @@ -692,10 +757,13 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { int idx = 0; Object[] item = new Object[6]; + int dataItemLength = 6; while (idx < data.Length) { - Array.Copy(data, idx, item, 0, 6); + dataItemLength = (idx + 7 == data.Length || (idx + 7 < data.Length && data[idx + 7] is bool)) ? 7 : 6; + item = new Object[dataItemLength]; + Array.Copy(data, idx, item, 0, dataItemLength); ListenerInfo info = ListenerInfo.FromData(localID, itemID, hostID, item); @@ -707,7 +775,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_listeners[(int)item[2]].Add(info); } - idx+=6; + idx+=dataItemLength; } } } @@ -723,19 +791,33 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm private UUID m_id; // ID to filter messages from private string m_name; // Object name to filter messages from private string m_message; // The message + private int m_regexBitfield; // The regex bitfield public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message) { - Initialise(handle, localID, ItemID, hostID, channel, name, id, message); + Initialise(handle, localID, ItemID, hostID, channel, name, id, + message, 0); + } + + public ListenerInfo(int handle, uint localID, UUID ItemID, + UUID hostID, int channel, string name, UUID id, + string message, int regexBitfield) + { + Initialise(handle, localID, ItemID, hostID, channel, name, id, + message, regexBitfield); } public ListenerInfo(ListenerInfo li, string name, UUID id, string message) { - Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message); + Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, 0); + } + + public ListenerInfo(ListenerInfo li, string name, UUID id, string message, int regexBitfield) + { + Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, regexBitfield); } - private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, - UUID id, string message) + private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message, int regexBitfield) { m_active = true; m_handle = handle; @@ -746,11 +828,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_name = name; m_id = id; m_message = message; + m_regexBitfield = regexBitfield; } public Object[] GetSerializationData() { - Object[] data = new Object[6]; + Object[] data = new Object[7]; data[0] = m_active; data[1] = m_handle; @@ -758,16 +841,19 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm data[3] = m_name; data[4] = m_id; data[5] = m_message; + data[6] = m_regexBitfield; return data; } public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data) { - ListenerInfo linfo = new ListenerInfo((int)data[1], localID, - ItemID, hostID, (int)data[2], (string)data[3], - (UUID)data[4], (string)data[5]); - linfo.m_active=(bool)data[0]; + ListenerInfo linfo = new ListenerInfo((int)data[1], localID, ItemID, hostID, (int)data[2], (string)data[3], (UUID)data[4], (string)data[5]); + linfo.m_active = (bool)data[0]; + if (data.Length >= 7) + { + linfo.m_regexBitfield = (int)data[6]; + } return linfo; } @@ -826,5 +912,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { return m_id; } + + public int GetRegexBitfield() + { + return m_regexBitfield; + } } } diff --git a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs index 8d88065..66b3f3a 100644 --- a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs +++ b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs @@ -45,6 +45,14 @@ namespace OpenSim.Region.Framework.Interfaces void Deactivate(); void Activate(); UUID GetID(); + + /// + /// Bitfield indicating which strings should be processed as regex. + /// 1 corresponds to IWorldCommListenerInfo::GetName() + /// 2 corresponds to IWorldCommListenerInfo::GetMessage() + /// + /// + int GetRegexBitfield(); } public interface IWorldComm @@ -70,6 +78,23 @@ namespace OpenSim.Region.Framework.Interfaces /// number of the scripts handle int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); + /// + /// Create a listen event callback with the specified filters. + /// The parameters localID,itemID are needed to uniquely identify + /// the script during 'peek' time. Parameter hostID is needed to + /// determine the position of the script. + /// + /// localID of the script engine + /// UUID of the script engine + /// UUID of the SceneObjectPart + /// channel to listen on + /// name to filter on + /// key to filter on (user given, could be totally faked) + /// msg to filter on + /// Bitfield indicating which strings should be processed as regex. + /// number of the scripts handle + int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg, int regexBitfield); + /// /// This method scans over the objects which registered an interest in listen callbacks. /// For everyone it finds, it checks if it fits the given filter. If it does, then diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 0650b90..828288d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -3647,5 +3647,68 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api DropAttachmentAt(false, pos, rot); } + + public LSL_Integer osListenRegex(int channelID, string name, string ID, string msg, int regexBitfield) + { + CheckThreatLevel(ThreatLevel.Low, "osListenRegex"); + m_host.AddScriptLPS(1); + UUID keyID; + UUID.TryParse(ID, out keyID); + + // if we want the name to be used as a regular expression, ensure it is valid first. + if ((regexBitfield & ScriptBaseClass.OS_LISTEN_REGEX_NAME) == ScriptBaseClass.OS_LISTEN_REGEX_NAME) + { + try + { + Regex.IsMatch("", name); + } + catch (Exception) + { + OSSLShoutError("Name regex is invalid."); + return -1; + } + } + + // if we want the msg to be used as a regular expression, ensure it is valid first. + if ((regexBitfield & ScriptBaseClass.OS_LISTEN_REGEX_MESSAGE) == ScriptBaseClass.OS_LISTEN_REGEX_MESSAGE) + { + try + { + Regex.IsMatch("", msg); + } + catch (Exception) + { + OSSLShoutError("Message regex is invalid."); + return -1; + } + } + + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); + return (wComm == null) ? -1 : wComm.Listen( + m_host.LocalId, + m_item.ItemID, + m_host.UUID, + channelID, + name, + keyID, + msg, + regexBitfield + ); + } + + public LSL_Integer osRegexIsMatch(string input, string pattern) + { + CheckThreatLevel(ThreatLevel.Low, "osRegexIsMatch"); + m_host.AddScriptLPS(1); + try + { + return Regex.IsMatch(input, pattern) ? 1 : 0; + } + catch (Exception) + { + OSSLShoutError("Possible invalid regular expression detected."); + return 0; + } + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 93188c9..cdd9ea8 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -418,5 +418,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces /// /// void osForceDropAttachmentAt(vector pos, rotation rot); + + /// + /// Identical to llListen except for a bitfield which indicates which + /// string parameters should be parsed as regex patterns. + /// + /// + /// + /// + /// + /// + /// OS_LISTEN_REGEX_NAME + /// OS_LISTEN_REGEX_MESSAGE + /// + /// + LSL_Integer osListenRegex(int channelID, string name, string ID, + string msg, int regexBitfield); + + /// + /// Wraps to bool Regex.IsMatch(string input, string pattern) + /// + /// string to test for match + /// string to use as pattern + /// boolean + LSL_Integer osRegexIsMatch(string input, string pattern); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs index 62bd6b8..880841b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs @@ -716,5 +716,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public static readonly LSLInteger RCERR_UNKNOWN = -1; public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2; public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3; + + /// + /// process name parameter as regex + /// + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// + /// process message parameter as regex + /// + public const int OS_LISTEN_REGEX_MESSAGE = 0x2; } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index dee1b28..afa9ae0 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -992,5 +992,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { m_OSSL_Functions.osForceDropAttachmentAt(pos, rot); } + + public LSL_Integer osListenRegex(int channelID, string name, string ID, string msg, int regexBitfield) + { + return m_OSSL_Functions.osListenRegex(channelID, name, ID, msg, regexBitfield); + } + + public LSL_Integer osRegexIsMatch(string input, string pattern) + { + return m_OSSL_Functions.osRegexIsMatch(input, pattern); + } } } -- cgit v1.1