diff --git a/plugins/Note/plugin.py b/plugins/Note/plugin.py index b89de964d..f76921a62 100644 --- a/plugins/Note/plugin.py +++ b/plugins/Note/plugin.py @@ -42,6 +42,7 @@ import supybot.ircmsgs as ircmsgs import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot import commands from supybot.i18n import PluginInternationalization, internationalizeDocstring _ = PluginInternationalization('Note') @@ -294,7 +295,8 @@ class Note(callbacks.Plugin): own = to for (option, arg) in optlist: if option == 'regexp': - criteria.append(arg.search) + criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg, + timeout=0.1, plugin_name = self.name(), fcn_name='search')) elif option == 'sent': own = frm if glob: diff --git a/plugins/Todo/plugin.py b/plugins/Todo/plugin.py index 91a2f98e8..90065e83a 100644 --- a/plugins/Todo/plugin.py +++ b/plugins/Todo/plugin.py @@ -41,6 +41,7 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot import commands from supybot.i18n import PluginInternationalization, internationalizeDocstring _ = PluginInternationalization('Todo') @@ -237,6 +238,8 @@ class Todo(callbacks.Plugin): criteria = [] for (option, arg) in optlist: if option == 'regexp': + criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg, + timeout=0.1, plugin_name = self.name(), fcn_name='search')) criteria.append(arg.search) for glob in globs: glob = utils.python.glob2re(glob) diff --git a/plugins/__init__.py b/plugins/__init__.py index 2e9b2c14f..5c86f5162 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -50,6 +50,7 @@ import supybot.world as world from supybot.commands import * import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot import commands ## i think we don't need any of this with sqlite3 #try: @@ -431,7 +432,9 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): if opt == 'by': predicates.append(lambda r, arg=arg: r.by == arg.id) elif opt == 'regexp': - predicates.append(lambda r, arg=arg: arg.search(r.text)) + predicates.append(lambda x: commands.regexp_wrapper(x.text, reobj=arg, + timeout=0.1, plugin_name = self.name(), fcn_name='search')) + #predicates.append(lambda r, arg=arg: arg.search(r.text)) if glob: def globP(r, glob=glob.lower()): return fnmatch.fnmatch(r.text.lower(), glob) diff --git a/src/commands.py b/src/commands.py index f1ec2ded6..4c26e29a6 100644 --- a/src/commands.py +++ b/src/commands.py @@ -106,6 +106,24 @@ def process(f, *args, **kwargs): v = "Error: " + str(v) return v +def regexp_wrapper(s, reobj, timeout, plugin_name, fcn_name): + '''A convenient wrapper to stuff regexp search queries through a subprocess. + + This is used because specially-crafted regexps can use exponential time + and hang the bot.''' + def re_bool(s, reobj): + """Since we can't enqueue match objects into the multiprocessing queue, + we'll just wrap the function to return bools.""" + if reobj.search(s) is not None: + return True + else: + return False + try: + v = process(re_bool, s, reobj, timeout=timeout, pn=plugin_name, cn=fcn_name) + return v + except ProcessTimeoutError: + return False + class UrlSnarfThread(world.SupyThread): def __init__(self, *args, **kwargs): assert 'url' in kwargs diff --git a/src/version.py b/src/version.py index b9b43b32f..8b46f4516 100644 --- a/src/version.py +++ b/src/version.py @@ -1,3 +1,3 @@ """stick the various versioning attributes in here, so we only have to change them once.""" -version = '0.83.4.1+limnoria (2011-08-13T01:57:03+0200)' +version = '0.83.4.1+limnoria (2011-08-13T01:59:18+0200)'