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

integration
SignpostMarv 2012-10-23 15:42:16 +01:00 committed by Justin Clark-Casey (justincc)
parent 18b1ee6f37
commit e977761071
6 changed files with 242 additions and 19 deletions

View File

@ -28,6 +28,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions;
using Nini.Config; using Nini.Config;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
@ -170,12 +171,42 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
/// <param name="hostID">UUID of the SceneObjectPart</param> /// <param name="hostID">UUID of the SceneObjectPart</param>
/// <param name="channel">channel to listen on</param> /// <param name="channel">channel to listen on</param>
/// <param name="name">name to filter 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> /// <param name="msg">msg to filter on</param>
/// <returns>number of the scripts handle</returns> /// <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> /// <summary>
@ -465,10 +496,20 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_curlisteners = 0; 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? // 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) if (coll.Count > 0)
{ {
@ -485,7 +526,9 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
if (newHandle > 0) 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; List<ListenerInfo> listeners;
if (!m_listeners.TryGetValue(channel,out listeners)) if (!m_listeners.TryGetValue(channel,out listeners))
@ -626,6 +669,22 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
return -1; 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 // Theres probably a more clever and efficient way to
// do this, maybe with regex. // do this, maybe with regex.
// PM2008: Ha, one could even be smart and define a specialized Enumerator. // PM2008: Ha, one could even be smart and define a specialized Enumerator.
@ -651,7 +710,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{ {
continue; 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; continue;
} }
@ -659,7 +721,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{ {
continue; 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; continue;
} }
@ -692,10 +757,13 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{ {
int idx = 0; int idx = 0;
Object[] item = new Object[6]; Object[] item = new Object[6];
int dataItemLength = 6;
while (idx < data.Length) 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 info =
ListenerInfo.FromData(localID, itemID, hostID, item); ListenerInfo.FromData(localID, itemID, hostID, item);
@ -707,7 +775,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_listeners[(int)item[2]].Add(info); 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 UUID m_id; // ID to filter messages from
private string m_name; // Object name to filter messages from private string m_name; // Object name to filter messages from
private string m_message; // The message 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) 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) 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);
} }
private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, public ListenerInfo(ListenerInfo li, string name, UUID id, string message, int regexBitfield)
UUID id, string message) {
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, int regexBitfield)
{ {
m_active = true; m_active = true;
m_handle = handle; m_handle = handle;
@ -746,11 +828,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_name = name; m_name = name;
m_id = id; m_id = id;
m_message = message; m_message = message;
m_regexBitfield = regexBitfield;
} }
public Object[] GetSerializationData() public Object[] GetSerializationData()
{ {
Object[] data = new Object[6]; Object[] data = new Object[7];
data[0] = m_active; data[0] = m_active;
data[1] = m_handle; data[1] = m_handle;
@ -758,16 +841,19 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
data[3] = m_name; data[3] = m_name;
data[4] = m_id; data[4] = m_id;
data[5] = m_message; data[5] = m_message;
data[6] = m_regexBitfield;
return data; return data;
} }
public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data) public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data)
{ {
ListenerInfo linfo = new ListenerInfo((int)data[1], localID, ListenerInfo linfo = new ListenerInfo((int)data[1], localID, ItemID, hostID, (int)data[2], (string)data[3], (UUID)data[4], (string)data[5]);
ItemID, hostID, (int)data[2], (string)data[3], linfo.m_active = (bool)data[0];
(UUID)data[4], (string)data[5]); if (data.Length >= 7)
linfo.m_active=(bool)data[0]; {
linfo.m_regexBitfield = (int)data[6];
}
return linfo; return linfo;
} }
@ -826,5 +912,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{ {
return m_id; return m_id;
} }
public int GetRegexBitfield()
{
return m_regexBitfield;
}
} }
} }

View File

@ -45,6 +45,14 @@ namespace OpenSim.Region.Framework.Interfaces
void Deactivate(); void Deactivate();
void Activate(); void Activate();
UUID GetID(); 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 public interface IWorldComm
@ -70,6 +78,23 @@ namespace OpenSim.Region.Framework.Interfaces
/// <returns>number of the scripts handle</returns> /// <returns>number of the scripts handle</returns>
int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); 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> /// <summary>
/// This method scans over the objects which registered an interest in listen callbacks. /// 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 /// For everyone it finds, it checks if it fits the given filter. If it does, then

View File

@ -3647,5 +3647,68 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
DropAttachmentAt(false, pos, rot); 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;
}
}
} }
} }

View File

@ -418,5 +418,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
/// <param name="pos"></param> /// <param name="pos"></param>
/// <param name="rot"></param> /// <param name="rot"></param>
void osForceDropAttachmentAt(vector pos, rotation rot); 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);
} }
} }

View File

@ -716,5 +716,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public static readonly LSLInteger RCERR_UNKNOWN = -1; public static readonly LSLInteger RCERR_UNKNOWN = -1;
public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2; public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2;
public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3; 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;
} }
} }

View File

@ -992,5 +992,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
{ {
m_OSSL_Functions.osForceDropAttachmentAt(pos, rot); 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);
}
} }
} }