From 654703d902db5e25b299b77181e3a7becaf9a688 Mon Sep 17 00:00:00 2001 From: Arinerron Date: Sun, 22 Apr 2018 20:42:37 -0700 Subject: [PATCH] Start working on plugin manager --- onionr/default-plugins/pluginmanager/main.py | 126 +++++++++++++++++-- onionr/onionr.py | 8 +- onionr/onionrpluginapi.py | 9 ++ onionr/onionrplugins.py | 11 +- onionr/onionrutils.py | 6 + 5 files changed, 146 insertions(+), 14 deletions(-) diff --git a/onionr/default-plugins/pluginmanager/main.py b/onionr/default-plugins/pluginmanager/main.py index 115ae56f..9bf7229a 100644 --- a/onionr/default-plugins/pluginmanager/main.py +++ b/onionr/default-plugins/pluginmanager/main.py @@ -4,18 +4,123 @@ # useful libraries import logger, config +import os, sys, json -# useful functions +plugin_name = 'pluginmanager' -def installPlugin(): +keys_data = {'keys' : {}} + +# key functions + +def writeKeys(): + ''' + Serializes and writes the keystore in memory to file + ''' + + file = open(keys_file, 'w') + file.write(json.dumps(keys_data, indent=4, sort_keys=True)) + file.close() + +def readKeys(): + ''' + Loads the keystore into memory + ''' + + global keys_data + keys_data = json.loads(open(keys_file).read()) + return keys_data + +def getKey(plugin): + ''' + Returns the public key for a given plugin + ''' + + readKeys() + return (keys_data['keys'][plugin] if plugin in keys_data['keys'] else None) + +def saveKey(plugin, key): + ''' + Saves the public key for a plugin to keystore + ''' + + keys_data['keys'][plugin] = key + writeKeys() + +def check(): + ''' + Checks to make sure the keystore file still exists + ''' + + global keys_file + keys_file = pluginapi.plugins.get_data_folder(plugin_name) + 'keystore.json' + if not os.path.isfile(keys_file): + writeKeys() + +# command handlers + +def help(): + logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [public key/block hash]') + +def commandInstallPlugin(): + logger.warn('This feature is not functional or is still in development.') + if len(sys.argv) >= 3: + check() + + pluginname = sys.argv[2] + pkobh = None # public key or block hash + + if len(sys.argv) >= 4: + # public key or block hash specified + pkobh = sys.argv[3] + else: + # none specified, check if in config file + pkobh = getKey(pluginname) + + if pkobh is None: + logger.error('No key for this plugin found in keystore, please specify.') + help() + return True + + valid_hash = pluginapi.get_utils().validateHash(pkobh) + real_block = False + valid_key = pluginapi.get_utils().validatePubKey(pkobh) + real_key = False + + if valid_hash: + real_block = pluginapi.get_utils().hasBlock(pkobh) + elif valid_key: + real_key = pluginapi.get_utils().hasKey(pkobh) + + blockhash = None + + if valid_hash and not real_block: + logger.error('Block hash not found. Perhaps it has not been synced yet?') + logger.debug('Is valid hash, but does not belong to a known block.') + return True + elif valid_hash and real_block: + blockhash = str(pkobh) + logger.debug('Using block ' + blockhash + '...') + elif valid_key and not real_key: + logger.error('Public key not found. Try adding the node by address manually, if possible.') + logger.debug('Is valid key, but the key is not a known one.') + elif valid_key and real_key: + publickey = str(pkobh) + logger.debug('Using public key ' + publickey + '...') + + saveKey(pluginname, pkobh) + else: + logger.error('Unknown data \"' + str(pkobh) + '\"; must be public key or block hash.') + return + else: + help() + + return True + +def commandUninstallPlugin(): logger.info('This feature has not been created yet. Please check back later.') return -def uninstallPlugin(): - logger.info('This feature has not been created yet. Please check back later.') - return - -def searchPlugin(): +def commandSearchPlugin(): logger.info('This feature has not been created yet. Please check back later.') return @@ -24,11 +129,12 @@ def searchPlugin(): def on_init(api, data = None): global pluginapi pluginapi = api + check() # register some commands - api.commands.register(['install-plugin', 'installplugin', 'plugin-install', 'install', 'plugininstall'], installPlugin) - api.commands.register(['remove-plugin', 'removeplugin', 'plugin-remove', 'uninstall-plugin', 'uninstallplugin', 'plugin-uninstall', 'uninstall', 'remove', 'pluginremove'], uninstallPlugin) - api.commands.register(['search', 'filter-plugins', 'search-plugins', 'searchplugins', 'search-plugin', 'searchplugin', 'findplugin', 'find-plugin', 'filterplugin', 'plugin-search', 'pluginsearch'], searchPlugin) + api.commands.register(['install-plugin', 'installplugin', 'plugin-install', 'install', 'plugininstall'], commandInstallPlugin) + api.commands.register(['remove-plugin', 'removeplugin', 'plugin-remove', 'uninstall-plugin', 'uninstallplugin', 'plugin-uninstall', 'uninstall', 'remove', 'pluginremove'], commandUninstallPlugin) + api.commands.register(['search', 'filter-plugins', 'search-plugins', 'searchplugins', 'search-plugin', 'searchplugin', 'findplugin', 'find-plugin', 'filterplugin', 'plugin-search', 'pluginsearch'], commandSearchPlugin) # add help menus once the features are actually implemented diff --git a/onionr/onionr.py b/onionr/onionr.py index 395dcb75..7a941d0b 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -102,16 +102,20 @@ class Onionr: # Copy default plugins into plugins folder - if not os.path.exists('data/plugins/'): + if not os.path.exists(plugins.get_plugins_folder()): if os.path.exists('default-plugins/'): names = [f for f in os.listdir("default-plugins/") if not os.path.isfile(f)] - shutil.copytree('default-plugins/', 'data/plugins/') + shutil.copytree('default-plugins/', plugins.get_plugins_folder()) # Enable plugins for name in names: if not name in plugins.get_enabled_plugins(): plugins.enable(name, self) + for name in plugins.get_enabled_plugins(): + if not os.path.exists(plugins.get_plugin_data_folder(name)): + os.mkdir(plugins.get_plugin_data_folder(name)) + if not os.path.exists(self.onionrCore.peerDB): self.onionrCore.createPeerDB() pass diff --git a/onionr/onionrpluginapi.py b/onionr/onionrpluginapi.py index 3989a2bb..7aa2b891 100644 --- a/onionr/onionrpluginapi.py +++ b/onionr/onionrpluginapi.py @@ -75,6 +75,15 @@ class PluginAPI: def get_enabled_plugins(self): return plugins.get_enabled() + def get_folder(self, name = None, absolute = True): + return plugins.get_plugins_folder(name = name, absolute = absolute) + + def get_data_folder(self, name, absolute = True): + return plugins.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 diff --git a/onionr/onionrplugins.py b/onionr/onionrplugins.py index 13ca3b95..bdb7a073 100644 --- a/onionr/onionrplugins.py +++ b/onionr/onionrplugins.py @@ -204,12 +204,19 @@ def get_plugins_folder(name = None, absolute = True): path = _pluginsfolder else: # only allow alphanumeric characters - path = _pluginsfolder + re.sub('[^0-9a-zA-Z]+', '', 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 + 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) + 'data/' def check(): ''' diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py index 634770c4..9baa41be 100644 --- a/onionr/onionrutils.py +++ b/onionr/onionrutils.py @@ -228,6 +228,12 @@ class OnionrUtils: conn.close() return False + def hasKey(self, key): + ''' + Check for key in list of public keys + ''' + return key in self._core.listPeers() + def validateHash(self, data, length=64): ''' Validate if a string is a valid hex formatted hash