diff --git a/plugins/Note/plugin.py b/plugins/Note/plugin.py index 3fd3cb326..d41487cb3 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 class NoteRecord(dbi.Record): __fields__ = [ @@ -292,7 +293,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 b995140ba..d28454676 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 class TodoRecord(dbi.Record): __fields__ = [ @@ -228,6 +229,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 478dfcc56..9d3acd2d7 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 723eec3b2..846f03342 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+gribble (2011-06-27T14:41:49-0400)' +version = '0.83.4.1+gribble (2011-08-12T18:12:56-0400)'