mirror of
https://github.com/mgeeky/RedWarden
synced 2026-06-08 15:53:59 +00:00
163 lines
5.5 KiB
Python
163 lines
5.5 KiB
Python
#!/usr/bin/python3
|
|
import os
|
|
import sys
|
|
import inspect
|
|
from io import StringIO
|
|
import csv
|
|
|
|
from lib.proxylogger import ProxyLogger
|
|
|
|
|
|
#
|
|
# Plugin that attempts to load all of the supplied plugins from
|
|
# program launch options.
|
|
class PluginsLoader:
|
|
class InjectedLogger(ProxyLogger):
|
|
def __init__(self, name, options = None):
|
|
self.name = name
|
|
super().__init__(options)
|
|
|
|
def _text(self, txt):
|
|
return '[{}] {}'.format(self.name, txt)
|
|
|
|
# Info shall be used as an ordinary logging facility, for every desired output.
|
|
def info(self, txt, forced = False, **kwargs):
|
|
super().info(self._text(txt), forced, **kwargs)
|
|
|
|
# Trace by default does not uses [TRACE] prefix. Shall be used
|
|
# for dumping packets, headers, metadata and longer technical output.
|
|
def trace(self, txt, **kwargs):
|
|
super().trace(self._text(txt), **kwargs)
|
|
|
|
def dbg(self, txt, **kwargs):
|
|
super().dbg(self._text(txt), **kwargs)
|
|
|
|
def err(self, txt, **kwargs):
|
|
super().err(self._text(txt), **kwargs)
|
|
|
|
def fatal(self, txt, **kwargs):
|
|
super().fatal(self._text(txt), **kwargs)
|
|
|
|
def __init__(self, logger, options, instantiate = True):
|
|
self.options = options
|
|
self.plugins = {}
|
|
self.called = False
|
|
self.logger = logger
|
|
self.instantiate = instantiate
|
|
plugins_count = len(self.options['plugins'])
|
|
|
|
if plugins_count > 0:
|
|
self.logger.info('Loading %d plugin%s...' % (plugins_count, '' if plugins_count == 1 else 's'))
|
|
|
|
for plugin in self.options['plugins']:
|
|
self.load(plugin)
|
|
|
|
self.called = True
|
|
|
|
# Output format:
|
|
# plugins = {'plugin1': instance, 'plugin2': instance, ...}
|
|
def get_plugins(self):
|
|
return self.plugins
|
|
|
|
#
|
|
# Following function parses input plugin path with parameters and decomposes
|
|
# them to extract plugin's arguments along with it's path.
|
|
# For instance, having such string:
|
|
# -p "plugins/my_plugin.py",argument1="test",argument2,argument3=test2
|
|
#
|
|
# It will return:
|
|
# {'path':'plugins/my_plugin.py', 'argument1':'t,e,s,t', 'argument2':'', 'argument3':'test2'}
|
|
#
|
|
@staticmethod
|
|
def decompose_path(p):
|
|
decomposed = {}
|
|
f = StringIO(p)
|
|
rows = list(csv.reader(f, quoting=csv.QUOTE_ALL, skipinitialspace=True))
|
|
|
|
for i in range(len(rows[0])):
|
|
row = rows[0][i]
|
|
if i == 0:
|
|
decomposed['path'] = row
|
|
continue
|
|
|
|
if '=' in row:
|
|
s = row.split('=')
|
|
decomposed[s[0]] = s[1].replace('"', '')
|
|
else:
|
|
decomposed[row] = ''
|
|
|
|
return decomposed
|
|
|
|
|
|
def load(self, path):
|
|
instance = None
|
|
|
|
self.logger.dbg('Plugin string: "%s"' % path)
|
|
decomposed = PluginsLoader.decompose_path(path)
|
|
self.logger.dbg('Decomposed as: %s' % str(decomposed))
|
|
|
|
plugin = decomposed['path'].strip()
|
|
|
|
if not os.path.isfile(plugin):
|
|
_plugin = os.path.normpath(os.path.join(os.path.dirname(__file__), '../plugins/{}'.format(plugin)))
|
|
if os.path.isfile(_plugin):
|
|
plugin = _plugin
|
|
elif os.path.isfile(_plugin+'.py'):
|
|
plugin = _plugin + '.py'
|
|
|
|
name = os.path.basename(plugin).lower().replace('.py', '')
|
|
|
|
if name in self.plugins or name in ['iproxyplugin', '__init__']:
|
|
# Plugin already loaded.
|
|
return
|
|
|
|
self.logger.dbg('Attempting to load plugin: %s ("%s")...' % (name, plugin))
|
|
|
|
try:
|
|
sys.path.append(os.path.dirname(plugin))
|
|
__import__(name)
|
|
module = sys.modules[name]
|
|
self.logger.dbg('Module imported.')
|
|
|
|
try:
|
|
handler = getattr(module, self.options['plugin_class_name'])
|
|
|
|
found = False
|
|
for base in inspect.getmro(handler):
|
|
if base.__name__ == 'IProxyPlugin':
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
raise TypeError('Plugin does not inherit from IProxyPlugin.')
|
|
|
|
# Call plugin's __init__ with the `logger' instance passed to it.
|
|
if self.instantiate:
|
|
instance = handler(PluginsLoader.InjectedLogger(name), self.options)
|
|
else:
|
|
instance = handler
|
|
|
|
self.logger.dbg('Found class "%s".' % self.options['plugin_class_name'])
|
|
|
|
except AttributeError as e:
|
|
self.logger.err('Plugin "%s" loading has failed: "%s".' %
|
|
(name, self.options['plugin_class_name']))
|
|
self.logger.err('\tError: %s' % e)
|
|
if self.options['debug']:
|
|
raise
|
|
|
|
except TypeError as e:
|
|
self.logger.err('Plugin "{}" instantiation failed due to interface incompatibility.'.format(name))
|
|
raise
|
|
|
|
if not instance:
|
|
self.logger.err('Didn\'t find supported class in module "%s"' % name)
|
|
else:
|
|
self.plugins[name] = instance
|
|
self.logger.info('Plugin "%s" has been installed.' % name)
|
|
|
|
except ImportError as e:
|
|
self.logger.err('Couldn\'t load specified plugin: "%s". Error: %s' % (plugin, e))
|
|
if self.options['debug']:
|
|
raise
|