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 /// <param name="hostID">UUID of the SceneObjectPart</param> /// <param name="channel">channel to listen on</param> /// <param name="name">name to filter on</param> - /// <param name="id">key to filter on (user given, could be totally faked)</param> + /// <param name="id"> + /// key to filter on (user given, could be totally faked) + /// </param> /// <param name="msg">msg to filter on</param> /// <returns>number of the scripts handle</returns> - 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); + } + + /// <summary> + /// 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. + /// </summary> + /// <param name="localID">localID of the script engine</param> + /// <param name="itemID">UUID of the script engine</param> + /// <param name="hostID">UUID of the SceneObjectPart</param> + /// <param name="channel">channel to listen on</param> + /// <param name="name">name to filter on</param> + /// <param name="id"> + /// key to filter on (user given, could be totally faked) + /// </param> + /// <param name="msg">msg to filter on</param> + /// <param name="regexBitfield"> + /// Bitfield indicating which strings should be processed as regex. + /// </param> + /// <returns>number of the scripts handle</returns> + 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); } /// <summary> @@ -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<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg); + List<ListenerInfo> 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<ListenerInfo> 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 + + /// <summary> + /// process name parameter as regex + /// </summary> + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// <summary> + /// process message parameter as regex + /// </summary> + 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(); + + /// <summary> + /// Bitfield indicating which strings should be processed as regex. + /// 1 corresponds to IWorldCommListenerInfo::GetName() + /// 2 corresponds to IWorldCommListenerInfo::GetMessage() + /// </summary> + /// <returns></returns> + int GetRegexBitfield(); } public interface IWorldComm @@ -70,6 +78,23 @@ namespace OpenSim.Region.Framework.Interfaces /// <returns>number of the scripts handle</returns> int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); + /// <summary> + /// 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. + /// </summary> + /// <param name="LocalID">localID of the script engine</param> + /// <param name="itemID">UUID of the script engine</param> + /// <param name="hostID">UUID of the SceneObjectPart</param> + /// <param name="channel">channel to listen on</param> + /// <param name="name">name to filter on</param> + /// <param name="id">key to filter on (user given, could be totally faked)</param> + /// <param name="msg">msg to filter on</param> + /// <param name="regexBitfield">Bitfield indicating which strings should be processed as regex.</param> + /// <returns>number of the scripts handle</returns> + int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg, int regexBitfield); + /// <summary> /// 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<IWorldComm>(); + 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 /// <param name="pos"></param> /// <param name="rot"></param> void osForceDropAttachmentAt(vector pos, rotation rot); + + /// <summary> + /// Identical to llListen except for a bitfield which indicates which + /// string parameters should be parsed as regex patterns. + /// </summary> + /// <param name="channelID"></param> + /// <param name="name"></param> + /// <param name="ID"></param> + /// <param name="msg"></param> + /// <param name="regexBitfield"> + /// OS_LISTEN_REGEX_NAME + /// OS_LISTEN_REGEX_MESSAGE + /// </param> + /// <returns></returns> + LSL_Integer osListenRegex(int channelID, string name, string ID, + string msg, int regexBitfield); + + /// <summary> + /// Wraps to bool Regex.IsMatch(string input, string pattern) + /// </summary> + /// <param name="input">string to test for match</param> + /// <param name="regex">string to use as pattern</param> + /// <returns>boolean</returns> + 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; + + /// <summary> + /// process name parameter as regex + /// </summary> + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// <summary> + /// process message parameter as regex + /// </summary> + 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