renamed onionr dir and bugfixes/linting progress
This commit is contained in:
parent
2b996da17f
commit
720efe4fca
226 changed files with 179 additions and 142 deletions
269
src/onionrplugins/__init__.py
Executable file
269
src/onionrplugins/__init__.py
Executable file
|
@ -0,0 +1,269 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file deals with management of modules/plugins.
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import os, re, importlib
|
||||
|
||||
from . import onionrevents as events
|
||||
import config, logger
|
||||
from utils import identifyhome
|
||||
|
||||
# set data dir
|
||||
dataDir = identifyhome.identify_home()
|
||||
|
||||
_pluginsfolder = dataDir + 'plugins/'
|
||||
_instances = dict()
|
||||
config.reload()
|
||||
|
||||
def reload(onionr = None, stop_event = True):
|
||||
'''
|
||||
Reloads all the plugins
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
try:
|
||||
enabled_plugins = get_enabled_plugins()
|
||||
|
||||
if stop_event is True:
|
||||
logger.debug('Reloading all plugins...')
|
||||
else:
|
||||
logger.debug('Loading all plugins...')
|
||||
|
||||
if stop_event is True:
|
||||
for plugin in enabled_plugins:
|
||||
stop(plugin, onionr)
|
||||
|
||||
for plugin in enabled_plugins:
|
||||
start(plugin, onionr)
|
||||
|
||||
return True
|
||||
except:
|
||||
logger.error('Failed to reload plugins.')
|
||||
|
||||
return False
|
||||
|
||||
def enable(name, onionr = None, start_event = True):
|
||||
'''
|
||||
Enables a plugin
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
if exists(name):
|
||||
enabled_plugins = get_enabled_plugins()
|
||||
if not name in enabled_plugins:
|
||||
try:
|
||||
events.call(get_plugin(name), 'enable', onionr)
|
||||
except ImportError as e: # Was getting import error on Gitlab CI test "data"
|
||||
# NOTE: If you are experiencing issues with plugins not being enabled, it might be this resulting from an error in the module
|
||||
# can happen inconsistently (especially between versions)
|
||||
logger.debug('Failed to enable module; Import error: %s' % e)
|
||||
return False
|
||||
else:
|
||||
enabled_plugins.append(name)
|
||||
config.set('plugins.enabled', enabled_plugins, savefile=True)
|
||||
|
||||
if start_event is True:
|
||||
start(name)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
logger.error('Failed to enable plugin \"%s\", disabling plugin.' % name, terminal=True)
|
||||
logger.debug('Plugins folder not found: %s' % get_plugins_folder(str(name).lower()))
|
||||
disable(name)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def disable(name, onionr = None, stop_event = True):
|
||||
'''
|
||||
Disables a plugin
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
if is_enabled(name):
|
||||
enabled_plugins = get_enabled_plugins()
|
||||
enabled_plugins.remove(name)
|
||||
config.set('plugins.enabled', enabled_plugins, True)
|
||||
|
||||
if exists(name):
|
||||
events.call(get_plugin(name), 'disable', onionr)
|
||||
|
||||
if stop_event is True:
|
||||
stop(name)
|
||||
|
||||
def start(name, onionr = None):
|
||||
'''
|
||||
Starts the plugin
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
if exists(name):
|
||||
try:
|
||||
plugin = get_plugin(name)
|
||||
|
||||
if plugin is None:
|
||||
raise Exception('Failed to import module.')
|
||||
else:
|
||||
events.call(plugin, 'start', onionr)
|
||||
|
||||
return plugin
|
||||
except:
|
||||
logger.error('Failed to start module \"%s\".' % name)
|
||||
else:
|
||||
logger.error('Failed to start nonexistant module \"%s\".' % name)
|
||||
|
||||
return None
|
||||
|
||||
def stop(name, onionr = None):
|
||||
'''
|
||||
Stops the plugin
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
if exists(name):
|
||||
try:
|
||||
plugin = get_plugin(name)
|
||||
|
||||
if plugin is None:
|
||||
raise Exception('Failed to import module.')
|
||||
else:
|
||||
events.call(plugin, 'stop', onionr)
|
||||
|
||||
return plugin
|
||||
except:
|
||||
logger.error('Failed to stop module \"%s\".' % name)
|
||||
else:
|
||||
logger.error('Failed to stop nonexistant module \"%s\".' % name)
|
||||
|
||||
return None
|
||||
|
||||
# credit: https://stackoverflow.com/a/29589414
|
||||
def import_module_from_file(full_path_to_module):
|
||||
"""
|
||||
Import a module given the full path/filename of the .py file
|
||||
|
||||
Python 3.4
|
||||
|
||||
"""
|
||||
|
||||
module = None
|
||||
|
||||
# Get module name and path from full path
|
||||
module_dir, module_file = os.path.split(full_path_to_module)
|
||||
module_name, module_ext = os.path.splitext(module_file)
|
||||
|
||||
module_name = module_dir # Module name must be unique otherwise it will get written in other imports
|
||||
|
||||
# Get module "spec" from filename
|
||||
spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)
|
||||
|
||||
module = spec.loader.load_module()
|
||||
|
||||
return module
|
||||
|
||||
def get_plugin(name):
|
||||
'''
|
||||
Returns the instance of a module
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
if str(name).lower() in _instances:
|
||||
return _instances[str(name).lower()]
|
||||
else:
|
||||
_instances[str(name).lower()] = import_module_from_file(get_plugins_folder(str(name).lower(), False) + 'main.py')
|
||||
return get_plugin(name)
|
||||
|
||||
def get_plugins():
|
||||
'''
|
||||
Returns a list of plugins (deprecated)
|
||||
'''
|
||||
|
||||
return _instances
|
||||
|
||||
def exists(name):
|
||||
'''
|
||||
Return value indicates whether or not the plugin exists
|
||||
'''
|
||||
|
||||
return os.path.isdir(get_plugins_folder(str(name).lower()))
|
||||
|
||||
def get_enabled_plugins():
|
||||
'''
|
||||
Returns a list of the enabled plugins
|
||||
'''
|
||||
|
||||
check()
|
||||
|
||||
return list(config.get('plugins.enabled', list()))
|
||||
|
||||
def is_enabled(name):
|
||||
'''
|
||||
Return value indicates whether or not the plugin is enabled
|
||||
'''
|
||||
|
||||
return name in get_enabled_plugins()
|
||||
|
||||
def get_plugins_folder(name = None, absolute = True):
|
||||
'''
|
||||
Returns the path to the plugins folder
|
||||
'''
|
||||
|
||||
path = ''
|
||||
|
||||
if name is None:
|
||||
path = _pluginsfolder
|
||||
else:
|
||||
# only allow alphanumeric characters
|
||||
#path = _pluginsfolder + str(name.lower())
|
||||
path = _pluginsfolder + re.sub('[^0-9a-zA-Z_]+', '', str(name).lower())
|
||||
|
||||
if absolute is True:
|
||||
path = os.path.abspath(path)
|
||||
|
||||
return path + '/'
|
||||
|
||||
def get_plugin_data_folder(name, absolute = True):
|
||||
'''
|
||||
Returns the location of a plugin's data folder
|
||||
'''
|
||||
|
||||
return get_plugins_folder(name, absolute)
|
||||
|
||||
def check():
|
||||
'''
|
||||
Checks to make sure files exist
|
||||
'''
|
||||
|
||||
if not config.is_set('plugins'):
|
||||
logger.debug('Generating plugin configuration data...')
|
||||
config.set('plugins', {'enabled': []}, True)
|
||||
|
||||
if not os.path.exists(os.path.dirname(get_plugins_folder())):
|
||||
logger.debug('Generating plugin data folder...')
|
||||
try:
|
||||
os.makedirs(os.path.dirname(get_plugins_folder()))
|
||||
except FileExistsError:
|
||||
pass
|
||||
return
|
75
src/onionrplugins/onionrevents.py
Executable file
75
src/onionrplugins/onionrevents.py
Executable file
|
@ -0,0 +1,75 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file deals with configuration management.
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
from threading import Thread
|
||||
|
||||
import config, logger
|
||||
import onionrplugins as plugins
|
||||
from . import onionrpluginapi as pluginapi
|
||||
|
||||
|
||||
def get_pluginapi(data):
|
||||
return pluginapi.SharedAPI(data)
|
||||
|
||||
def __event_caller(event_name, data = {}):
|
||||
'''
|
||||
DO NOT call this function, this is for threading code only.
|
||||
Instead, call onionrevents.event
|
||||
'''
|
||||
for plugin in plugins.get_enabled_plugins():
|
||||
try:
|
||||
call(plugins.get_plugin(plugin), event_name, data, get_pluginapi(data))
|
||||
except ModuleNotFoundError as e:
|
||||
logger.warn('Disabling nonexistant plugin "%s"...' % plugin, terminal=True)
|
||||
plugins.disable(plugin, stop_event = False)
|
||||
except Exception as e:
|
||||
logger.warn('Event "%s" failed for plugin "%s".' % (event_name, plugin), terminal=True)
|
||||
logger.debug(str(e), terminal=True)
|
||||
|
||||
def event(event_name, data = {}, threaded = True):
|
||||
'''
|
||||
Calls an event on all plugins (if defined)
|
||||
'''
|
||||
|
||||
if threaded:
|
||||
thread = Thread(target = __event_caller, args = (event_name, data))
|
||||
thread.start()
|
||||
return thread
|
||||
else:
|
||||
__event_caller(event_name, data)
|
||||
|
||||
def call(plugin, event_name, data = None, pluginapi = None):
|
||||
'''
|
||||
Calls an event on a plugin if one is defined
|
||||
'''
|
||||
|
||||
if not plugin is None:
|
||||
try:
|
||||
attribute = 'on_' + str(event_name).lower()
|
||||
if pluginapi is None:
|
||||
pluginapi = get_pluginapi(data)
|
||||
if hasattr(plugin, attribute):
|
||||
return getattr(plugin, attribute)(pluginapi, data)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
#logger.error(str(e), terminal=True)
|
||||
return False
|
||||
else:
|
||||
return True
|
81
src/onionrplugins/onionrpluginapi.py
Executable file
81
src/onionrplugins/onionrpluginapi.py
Executable file
|
@ -0,0 +1,81 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file deals with the object that is passed with each event
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import onionrplugins, logger
|
||||
from onionrutils import localcommand
|
||||
from coredb import daemonqueue
|
||||
|
||||
class PluginAPI:
|
||||
def __init__(self, pluginapi):
|
||||
self.pluginapi = pluginapi
|
||||
|
||||
def start(self, name):
|
||||
onionrplugins.start(name)
|
||||
|
||||
def stop(self, name):
|
||||
onionrplugins.stop(name)
|
||||
|
||||
def reload(self, name):
|
||||
onionrplugins.reload(name)
|
||||
|
||||
def enable(self, name):
|
||||
onionrplugins.enable(name)
|
||||
|
||||
def disable(self, name):
|
||||
onionrplugins.disable(name)
|
||||
|
||||
def event(self, name, data = {}):
|
||||
events.event(name, data = data)
|
||||
|
||||
def is_enabled(self, name):
|
||||
return onionrplugins.is_enabled(name)
|
||||
|
||||
def get_enabled_plugins(self):
|
||||
return onionrplugins.get_enabled()
|
||||
|
||||
def get_folder(self, name = None, absolute = True):
|
||||
return onionrplugins.get_plugins_folder(name = name, absolute = absolute)
|
||||
|
||||
def get_data_folder(self, name, absolute = True):
|
||||
return onionrplugins.get_plugin_data_folder(name, absolute = absolute)
|
||||
|
||||
def daemon_event(self, event, plugin = None):
|
||||
return # later make local command like /client/?action=makeEvent&event=eventname&module=modulename
|
||||
|
||||
class CommandAPI:
|
||||
def __init__(self, pluginapi):
|
||||
self.pluginapi = pluginapi
|
||||
|
||||
def call(self, name):
|
||||
self.pluginapi.get_onionr().execute(name)
|
||||
|
||||
class SharedAPI:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.plugins = PluginAPI(self)
|
||||
|
||||
def get_data(self):
|
||||
return self.data
|
||||
|
||||
def get_daemonapi(self):
|
||||
return self.daemon
|
||||
|
||||
def get_pluginapi(self):
|
||||
return self.plugins
|
Loading…
Add table
Add a link
Reference in a new issue