Improve support for plugins

This commit is contained in:
Arinerron 2018-04-20 20:10:50 -07:00
parent 8d8f167d7f
commit 098abb8e55
No known key found for this signature in database
GPG Key ID: 99383627861C62F0
8 changed files with 241 additions and 135 deletions

View File

@ -22,7 +22,7 @@ import sqlite3, os, sys, time, math, base64, tarfile, getpass, simplecrypt, hash
#from Crypto import Random
import netcontroller
import onionrutils, onionrcrypto, btc
import onionrutils, onionrcrypto, btc, onionrevents as events
if sys.version_info < (3, 6):
try:
@ -89,10 +89,13 @@ class Core:
c.execute('INSERT INTO peers (id, name, dateSeen) VALUES(?, ?, ?);', t)
conn.commit()
conn.close()
return True
def addAddress(self, address):
'''Add an address to the address database (only tor currently)'''
'''
Add an address to the address database (only tor currently)
'''
if self._utils.validateID(address):
conn = sqlite3.connect(self.addressDB)
c = conn.cursor()
@ -114,12 +117,17 @@ class Core:
c.execute('INSERT INTO adders (address, type) VALUES(?, ?);', t)
conn.commit()
conn.close()
events.event('address_add', data = {'address': address}, onionr = None)
return True
else:
return False
def removeAddress(self, address):
'''Remove an address from the address database'''
'''
Remove an address from the address database
'''
if self._utils.validateID(address):
conn = sqlite3.connect(self.addressDB)
c = conn.cursor()
@ -127,6 +135,9 @@ class Core:
c.execute('Delete from adders where address=?;', t)
conn.commit()
conn.close()
events.event('address_remove', data = {'address': address}, onionr = None)
return True
else:
return False
@ -330,6 +341,8 @@ class Core:
conn.commit()
conn.close()
events.event('queue_pop', data = {'data': retData}, onionr = None)
return retData
def daemonQueueAdd(self, command, data=''):
@ -345,6 +358,8 @@ class Core:
conn.commit()
conn.close()
events.event('queue_push', data = {'command': command, 'data': data}, onionr = None)
return
def clearDaemonQueue(self):
@ -354,11 +369,12 @@ class Core:
conn = sqlite3.connect(self.queueDB)
c = conn.cursor()
try:
c.execute('delete from commands;')
c.execute('DELETE FROM commands;')
conn.commit()
except:
pass
conn.close()
events.event('queue_clear', onionr = None)
return
@ -564,4 +580,5 @@ class Core:
announceAmount = len(nodeList)
for i in range(announceAmount):
self.daemonQueueAdd('announceNode', nodeList[i])
events.event('introduction', onionr = None)
return

41
onionr/default_plugin.txt Normal file
View File

@ -0,0 +1,41 @@
'''
Default plugin template file
Generated on $date by $user.
'''
# Imports some useful libraries
import logger, config
def on_init(api, data = None):
'''
This event is called after Onionr is initialized, but before the command
inputted is executed. Could be called when daemon is starting or when
just the client is running.
'''
# Doing this makes it so that the other functions can access the api object
# by simply referencing the variable `pluginapi`.
global pluginapi
pluginapi = api
return
def on_start(api, data = None):
'''
This event can be called for multiple reasons:
1) The daemon is starting
2) The user called `onionr --start-plugins` or `onionr --reload-plugins`
3) For whatever reason, the plugins are reloading
'''
return
def on_stop(api, data = None):
'''
This event can be called for multiple reasons:
1) The daemon is stopping
2) The user called `onionr --stop-plugins` or `onionr --reload-plugins`
3) For whatever reason, the plugins are reloading
'''
return

View File

@ -20,8 +20,8 @@
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 sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform
import api, core, config, logger, onionrplugins as plugins
import sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform, datetime, re
import api, core, config, logger, onionrplugins as plugins, onionrevents as events
from onionrutils import OnionrUtils
from netcontroller import NetController
@ -137,6 +137,9 @@ class Onionr:
'reloadplugin': self.reloadPlugin,
'reload-plugins': self.reloadPlugin,
'reloadplugins': self.reloadPlugin,
'create-plugin': self.createPlugin,
'createplugin': self.createPlugin,
'plugin-create': self.createPlugin,
'listkeys': self.listKeys,
'list-keys': self.listKeys,
@ -159,6 +162,7 @@ class Onionr:
'addaddr': self.addAddress,
'addaddress': self.addAddress,
'introduce': self.onionrCore.introduceNode,
'connect': self.addAddress
}
@ -172,14 +176,19 @@ class Onionr:
'enable-plugin': 'Enables and starts a plugin',
'disable-plugin': 'Disables and stops a plugin',
'reload-plugin': 'Reloads a plugin',
'create-plugin': 'Creates directory structure for a plugin',
'add-peer': 'Adds a peer (?)',
'list-peers': 'Displays a list of peers',
'add-msg': 'Broadcasts a message to the Onionr network',
'pm': 'Adds a private message to block',
'get-pms': 'Shows private messages sent to you',
'gui': 'Opens a graphical interface for Onionr'
'gui': 'Opens a graphical interface for Onionr',
'introduce': 'Introduce your node to the public Onionr network (DAEMON MUST BE RUNNING)',
}
# initialize plugins
events.event('init', onionr = self)
command = ''
try:
command = sys.argv[1].lower()
@ -238,6 +247,7 @@ class Onionr:
'''
Executes a command
'''
argument = argument[argument.startswith('--') and len('--'):] # remove -- if it starts with it
# define commands
@ -256,6 +266,7 @@ class Onionr:
'''
Displays the Onionr version
'''
logger.info('Onionr ' + ONIONR_VERSION + ' (' + platform.machine() + ') - API v' + API_VERSION)
if verbosity >= 1:
logger.info(ONIONR_TAGLINE)
@ -268,6 +279,7 @@ class Onionr:
'''
Create a private message and send it
'''
invalidID = True
while invalidID:
try:
@ -404,6 +416,34 @@ class Onionr:
return
def createPlugin(self):
'''
Creates the directory structure for a plugin name
'''
if len(sys.argv) >= 3:
try:
plugin_name = re.sub('[^0-9a-zA-Z]+', '', str(sys.argv[2]).lower())
if not plugins.exists(plugin_name):
logger.info('Creating plugin \"' + plugin_name + '\"...')
os.makedirs(plugins.get_plugins_folder(plugin_name))
with open(plugins.get_plugins_folder(plugin_name) + '/main.py', 'a') as main:
main.write(open('default_plugin.txt').read().replace('$user', os.getlogin()).replace('$date', datetime.datetime.now().strftime('%Y-%m-%d')))
logger.info('Enabling plugin \"' + plugin_name + '\"...')
plugins.enable(plugin_name, self)
else:
logger.warn('Cannot create plugin directory structure; plugin "' + plugin_name + '" exists.')
except Exception as e:
logger.error('Failed to create plugin directory structure.', e)
else:
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin>')
return
def notFound(self):
'''
Displays a "command not found" message
@ -451,6 +491,7 @@ class Onionr:
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
logger.debug('Started communicator')
events.event('daemon_start', onionr = self)
api.API(self.debug)
return
@ -461,6 +502,7 @@ class Onionr:
'''
logger.warn('Killing the running daemon')
events.event('daemon_stop', onionr = self)
net = NetController(config.get('client')['port'])
try:
self.onionrUtils.localCommand('shutdown')

View File

@ -20,19 +20,20 @@
import config, logger, onionrplugins as plugins, onionrpluginapi as pluginapi
def get_pluginapi(onionr):
return pluginapi.PluginAPI(onionr)
def get_pluginapi(onionr, data):
return pluginapi.pluginapi(onionr, data)
def event(event_name, data = None, onionr = None):
def event(event_name, data = {}, onionr = None):
'''
Calls an event on all plugins (if defined)
'''
for plugin in plugins.get_enabled_plugins():
try:
call(plugins.get_plugin(plugin), event_name, data, self.get_pluginapi(onionr))
except:
call(plugins.get_plugin(plugin), event_name, data, get_pluginapi(onionr, data))
except Exception as e:
logger.warn('Event \"' + event_name + '\" failed for plugin \"' + plugin + '\".')
logger.debug(str(e))
def call(plugin, event_name, data = None, pluginapi = None):
'''
@ -45,12 +46,12 @@ def call(plugin, event_name, data = None, pluginapi = None):
# TODO: Use multithreading perhaps?
if hasattr(plugin, attribute):
logger.debug('Calling event ' + str(event_name))
getattr(plugin, attribute)(pluginapi, data)
#logger.debug('Calling event ' + str(event_name))
getattr(plugin, attribute)(pluginapi)
return True
except:
logger.warn('Failed to call event ' + str(event_name) + ' on module.')
except Exception as e:
logger.debug(str(e))
return False
else:
return True

View File

@ -20,131 +20,135 @@
import onionrplugins as plugins, logger
class DaemonAPI:
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def start(self):
self.pluginapi.get_onionr().daemon()
return
def stop(self):
self.pluginapi.get_onionr().killDaemon()
return
def queue(self, command, data = ''):
self.pluginapi.get_core().daemonQueueAdd(command, data)
return
def local_command(self, command):
self.pluginapi.get_utils().localCommand(self, command)
return
def queue_pop(self):
return self.get_core().daemonQueue()
class PluginAPI:
def __init__(self, onionr):
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def start(self, name):
plugins.start(name)
def stop(self, name):
plugins.stop(name)
def reload(self, name):
plugins.reload(name)
def enable(self, name):
plugins.enable(name)
def disable(self, name):
plugins.disable(name)
def is_enabled(self, name):
return plugins.is_enabled(name)
def get_enabled_plugins(self):
return plugins.get_enabled_plugins()
class CommandAPI:
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def register(self, names, call = None):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().addCommand(name, call)
return
def unregister(self, names):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().delCommand(name)
return
def register_help(self, names, description):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().addHelp(name, description)
return
def unregister_help(self, names):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().delHelp(name)
return
def call(self, name):
self.pluginapi.get_onionr().execute(name)
return
def get_commands(self):
return self.pluginapi.get_onionr().getCommands()
class pluginapi:
def __init__(self, onionr, data):
self.onionr = onionr
self.data = data
self.daemon = DaemonAPI(self)
self.plugin = PluginAPI(self)
self.command = CommandAPI(self)
self.plugins = PluginAPI(self)
self.commands = CommandAPI(self)
def get_onionr(self):
return self.onionr
def get_data(self):
return self.data
def get_core(self):
return self.get_onionr().onionrCore
def get_utils(self):
return self.get_onionr().onionrUtils
def get_daemonapi(self):
return self.daemon
def get_pluginapi(self):
return self.plugin
return self.plugins
def get_commandapi(self):
return self.command
return self.commands
def is_development_mode(self):
return self.get_onionr()._developmentMode
class DaemonAPI:
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def start(self):
self.pluginapi.get_onionr().daemon()
return
def stop(self):
self.pluginapi.get_onionr().killDaemon()
return
def queue(self, command, data = ''):
self.pluginapi.get_core().daemonQueueAdd(command, data)
return
def local_command(self, command):
self.pluginapi.get_utils().localCommand(self, command)
return
def queue_pop(self):
return self.get_core().daemonQueue()
class PluginAPI:
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def start(self, name):
plugins.start(name)
def stop(self, name):
plugins.stop(name)
def reload(self, name):
plugins.reload(name)
def enable(self, name):
plugins.enable(name)
def disable(self, name):
plugins.disable(name)
def is_enabled(self, name):
return plugins.is_enabled(name)
def get_enabled_plugins(self):
return plugins.get_enabled_plugins()
class CommandAPI:
def __init__(self, pluginapi):
self.pluginapi = pluginapi
def register(self, names, call = None):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().addCommand(name, call)
return
def unregister(self, names):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().delCommand(name)
return
def register_help(self, names, description):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().addHelp(name, description)
return
def unregister_help(self, names):
if isinstance(names, str):
names = [names]
for name in names:
self.pluginapi.get_onionr().delHelp(name)
return
def call(self, name):
self.pluginapi.get_onionr().execute(name)
return
def get_commands(self):
return self.pluginapi.get_onionr().getCommands()

View File

@ -225,5 +225,5 @@ def check():
if not os.path.exists(os.path.dirname(get_plugins_folder())):
logger.debug('Generating plugin data folder...')
os.makedirs(os.path.dirname(get_plugins_folder()))
return

View File

@ -129,7 +129,7 @@ class OnionrUtils:
logger.error('Failed to read my address.', error=error)
return ''
def localCommand(self, command):
def localCommand(self, command, silent = True):
'''
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
'''
@ -140,7 +140,8 @@ class OnionrUtils:
try:
retData = requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('client')['port']) + '/client/?action=' + command + '&token=' + str(config.get('client')['client_hmac']) + '&timingToken=' + self.timingToken).text
except Exception as error:
logger.error('Failed to make local request (command: ' + str(command) + ').', error=error)
if not silent:
logger.error('Failed to make local request (command: ' + str(command) + ').', error=error)
retData = False
return retData

View File

@ -134,7 +134,7 @@ class OnionrTests(unittest.TestCase):
if not onionrplugins.exists('test'):
os.makedirs(onionrplugins.get_plugins_folder('test'))
with open(onionrplugins.get_plugins_folder('test') + '/main.py', 'a') as main:
main.write("print('Running')\n\ndef on_test(onionr = None, data = None):\n print('received test event!')\n return True\n\ndef on_start(onionr = None, data = None):\n print('start event called')\n\ndef on_stop(onionr = None, data = None):\n print('stop event called')\n\ndef on_enable(onionr = None, data = None):\n print('enable event called')\n\ndef on_disable(onionr = None, data = None):\n print('disable event called')\n")
main.write("print('Running')\n\ndef on_test(pluginapi, data = None):\n print('received test event!')\n return True\n\ndef on_start(pluginapi, data = None):\n print('start event called')\n\ndef on_stop(pluginapi, data = None):\n print('stop event called')\n\ndef on_enable(pluginapi, data = None):\n print('enable event called')\n\ndef on_disable(pluginapi, data = None):\n print('disable event called')\n")
onionrplugins.enable('test')
try:
@ -152,7 +152,7 @@ class OnionrTests(unittest.TestCase):
if not onionrplugins.exists('test'):
os.makedirs(onionrplugins.get_plugins_folder('test'))
with open(onionrplugins.get_plugins_folder('test') + '/main.py', 'a') as main:
main.write("print('Running')\n\ndef on_test(onionr = None, data = None):\n print('received test event!')\n return True\n\ndef on_start(onionr = None, data = None):\n print('start event called')\n\ndef on_stop(onionr = None, data = None):\n print('stop event called')\n\ndef on_enable(onionr = None, data = None):\n print('enable event called')\n\ndef on_disable(onionr = None, data = None):\n print('disable event called')\n")
main.write("print('Running')\n\ndef on_test(pluginapi, data = None):\n print('received test event!')\n return True\n\ndef on_start(pluginapi, data = None):\n print('start event called')\n\ndef on_stop(pluginapi, data = None):\n print('stop event called')\n\ndef on_enable(pluginapi, data = None):\n print('enable event called')\n\ndef on_disable(pluginapi, data = None):\n print('disable event called')\n")
onionrplugins.enable('test')
try:
@ -171,7 +171,7 @@ class OnionrTests(unittest.TestCase):
if not plugins.exists('test'):
os.makedirs(plugins.get_plugins_folder('test'))
with open(plugins.get_plugins_folder('test') + '/main.py', 'a') as main:
main.write("print('Running')\n\ndef on_test(onionr = None, data = None):\n print('received test event!')\n return True\n\ndef on_start(onionr = None, data = None):\n print('start event called')\n\ndef on_stop(onionr = None, data = None):\n print('stop event called')\n\ndef on_enable(onionr = None, data = None):\n print('enable event called')\n\ndef on_disable(onionr = None, data = None):\n print('disable event called')\n")
main.write("print('Running')\n\ndef on_test(pluginapi, data = None):\n print('received test event!')\n return True\n\ndef on_start(pluginapi, data = None):\n print('start event called')\n\ndef on_stop(pluginapi, data = None):\n print('stop event called')\n\ndef on_enable(pluginapi, data = None):\n print('enable event called')\n\ndef on_disable(pluginapi, data = None):\n print('disable event called')\n")
plugins.enable('test')