From aeb9a6e77554aa9cd4236c1bb938a95268efe008 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Sun, 6 Jan 2019 23:50:20 -0600 Subject: [PATCH] work on gui, dbstorage, daemon queue responses --- onionr/api.py | 17 +++++++++- onionr/communicator2.py | 8 +++-- onionr/config.py | 2 +- onionr/core.py | 18 ++++++---- onionr/dbcreator.py | 3 +- onionr/onionr.py | 11 ++++-- onionr/onionrblockapi.py | 11 ++++-- onionr/onionrgui.py | 34 +++++++++++++++---- onionr/onionrstorage.py | 4 ++- onionr/onionrutils.py | 8 +++-- .../static-data/default-plugins/flow/main.py | 9 +++-- 11 files changed, 95 insertions(+), 30 deletions(-) diff --git a/onionr/api.py b/onionr/api.py index 75f02a13..62467631 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -109,7 +109,7 @@ class PublicAPI: data = name if clientAPI._utils.validateHash(data): if data not in self.hideBlocks: - if os.path.exists(clientAPI._core.dataDir + 'blocks/' + data + '.dat'): + if data in clientAPI._core.getBlockList(): block = Block(hash=data.encode(), core=clientAPI._core) resp = base64.b64encode(block.getRaw().encode()).decode() if len(resp) == 0: @@ -244,6 +244,8 @@ class API: self.host = setBindIP(self._core.privateApiHostFile) logger.info('Running api on %s:%s' % (self.host, self.bindPort)) self.httpServer = '' + + self.queueResponse = {} onionrInst.setClientAPIInst(self) @app.before_request @@ -287,6 +289,19 @@ class API: abort(403) return send_from_directory(config.get('www.private.path', 'static-data/www/private/'), path) + @app.route('/queueResponseAdd/', methods=['post']) + def queueResponseAdd(name): + self.queueResponse[name] = request.form['data'] + return Response('success') + + @app.route('/queueResponse/') + def queueResponse(name): + try: + res = self.queueResponse[name] + except KeyError: + resp = '' + return Response(resp) + @app.route('/ping') def ping(): return Response("pong!") diff --git a/onionr/communicator2.py b/onionr/communicator2.py index bf99ff56..6adb1f2c 100755 --- a/onionr/communicator2.py +++ b/onionr/communicator2.py @@ -472,7 +472,7 @@ class OnionrCommunicatorDaemon: Process daemon commands from daemonQueue ''' cmd = self._core.daemonQueue() - + response = '' if cmd is not False: events.event('daemon_command', onionr = None, data = {'cmd' : cmd}) if cmd[0] == 'shutdown': @@ -486,7 +486,7 @@ class OnionrCommunicatorDaemon: logger.debug('Status check; looks good.') open(self._core.dataDir + '.runcheck', 'w+').close() elif cmd[0] == 'connectedPeers': - self.printOnlinePeers() + response = '\n'.join(list(self.onlinePeers)).strip() elif cmd[0] == 'pex': for i in self.timers: if i.timerFunction.__name__ == 'lookupAdders': @@ -507,6 +507,10 @@ class OnionrCommunicatorDaemon: else: logger.info('Recieved daemonQueue command:' + cmd[0]) + if cmd[4] != '': + if response != '': + self._core._utils.localCommand('queueResponseAdd', data='/' + cmd[4], post=True, postData=response) + self.decrementThreadCount('daemonCommands') def uploadBlock(self): diff --git a/onionr/config.py b/onionr/config.py index 7c986b83..3a358233 100644 --- a/onionr/config.py +++ b/onionr/config.py @@ -129,7 +129,7 @@ def reload(): with open(get_config_file(), 'r', encoding="utf8") as configfile: set_config(json.loads(configfile.read())) except: - logger.warn('Failed to parse configuration file.') + logger.debug('Failed to parse configuration file.') def get_config(): ''' diff --git a/onionr/core.py b/onionr/core.py index a4783d08..ce29a0a2 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -import sqlite3, os, sys, time, math, base64, tarfile, nacl, logger, json, netcontroller, math, config +import sqlite3, os, sys, time, math, base64, tarfile, nacl, logger, json, netcontroller, math, config, uuid from onionrblockapi import Block import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions, onionrvalues @@ -342,7 +342,7 @@ class Core: conn = sqlite3.connect(self.queueDB, timeout=10) c = conn.cursor() try: - for row in c.execute('SELECT command, data, date, min(ID) FROM commands group by id'): + for row in c.execute('SELECT command, data, date, min(ID), responseID FROM commands group by id'): retData = row break except sqlite3.OperationalError: @@ -357,21 +357,20 @@ class Core: return retData - def daemonQueueAdd(self, command, data=''): + def daemonQueueAdd(self, command, data='', responseID=''): ''' Add a command to the daemon queue, used by the communication daemon (communicator.py) ''' retData = True - # Intended to be used by the web server date = self._utils.getEpoch() conn = sqlite3.connect(self.queueDB, timeout=10) c = conn.cursor() - t = (command, data, date) + t = (command, data, date, responseID) try: - c.execute('INSERT INTO commands (command, data, date) VALUES(?, ?, ?)', t) + c.execute('INSERT INTO commands (command, data, date, responseID) VALUES(?, ?, ?, ?)', t) conn.commit() conn.close() except sqlite3.OperationalError: @@ -379,6 +378,13 @@ class Core: self.daemonQueue() events.event('queue_push', data = {'command': command, 'data': data}, onionr = None) return retData + + def daemonQueueGetResponse(self, responseID=''): + ''' + Get a response sent by communicator to the API, by requesting to the API + ''' + assert len(responseID) > 0 + resp = self._utils.localCommand('queueResponse', data='/' + responseID, post=True) def clearDaemonQueue(self): ''' diff --git a/onionr/dbcreator.py b/onionr/dbcreator.py index 19de9e84..f728254a 100644 --- a/onionr/dbcreator.py +++ b/onionr/dbcreator.py @@ -152,7 +152,6 @@ class DBCreator: conn = sqlite3.connect(self.core.queueDB, timeout=10) c = conn.cursor() # Create table - c.execute('''CREATE TABLE commands - (id integer primary key autoincrement, command text, data text, date text)''') + c.execute('''CREATE TABLE commands (id integer primary key autoincrement, command text, data text, date text, responseID text)''') conn.commit() conn.close() \ No newline at end of file diff --git a/onionr/onionr.py b/onionr/onionr.py index 04bee60a..979ad189 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -25,7 +25,7 @@ if sys.version_info[0] == 2 or sys.version_info[1] < 5: print('Error, Onionr requires Python 3.5+') sys.exit(1) import os, base64, random, getpass, shutil, subprocess, requests, time, platform, datetime, re, json, getpass, sqlite3 -import webbrowser +import webbrowser, uuid from threading import Thread import api, core, config, logger, onionrplugins as plugins, onionrevents as events import onionrutils @@ -394,7 +394,14 @@ class Onionr: return def listConn(self): - self.onionrCore.daemonQueueAdd('connectedPeers') + randID = str(uuid.uuid4()) + self.onionrCore.daemonQueueAdd('connectedPeers', responseID=randID) + while True: + time.sleep(1) + peers = self.onionrCore.daemonQueueGetResponse(randID) + if peers not in ('', None): + print(peers) + break def listPeers(self): logger.info('Peer transport address list:') diff --git a/onionr/onionrblockapi.py b/onionr/onionrblockapi.py index 39af3cee..c76934a5 100644 --- a/onionr/onionrblockapi.py +++ b/onionr/onionrblockapi.py @@ -240,12 +240,14 @@ class Block: try: if self.isValid() is True: + ''' if (not self.getBlockFile() is None) and (recreate is True): onionrstorage.store(self.core, self.getRaw().encode()) #with open(self.getBlockFile(), 'wb') as blockFile: # blockFile.write(self.getRaw().encode()) else: - self.hash = self.getCore().insertBlock(self.getContent(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire()) + ''' + self.hash = self.getCore().insertBlock(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire()) if self.hash != False: self.update() @@ -751,13 +753,16 @@ class Block: # no input data? scrap it. if hash is None: return False - + ''' if type(hash) == Block: blockfile = hash.getBlockFile() else: blockfile = onionrcore.Core().dataDir + 'blocks/%s.dat' % hash + ''' - return os.path.exists(blockfile) and os.path.isfile(blockfile) + ret = isinstance(onionrstorage.getData(onionrcore.Core(), hash.getHash()), type(None)) + + return not ret def getCache(hash = None): # give a list of the hashes of the cached blocks diff --git a/onionr/onionrgui.py b/onionr/onionrgui.py index 510ea594..41ff8fc0 100755 --- a/onionr/onionrgui.py +++ b/onionr/onionrgui.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import threading, time from tkinter import * import core class OnionrGUI: @@ -11,8 +12,7 @@ class OnionrGUI: # create a pulldown menu, and add it to the menu bar filemenu = Menu(menubar, tearoff=0) - filemenu.add_command(label="Open", command=None) - filemenu.add_command(label="Save", command=None) + #filemenu.add_command(label="Open", command=None) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.root.quit) menubar.add_cascade(label="File", menu=filemenu) @@ -26,16 +26,13 @@ class OnionrGUI: self.root.config(menu=menubar) self.menuFrame = Frame(self.root) - self.mainButton = Button(self.menuFrame, text="Main View") - self.mainButton.grid(row=0, column=0, padx=0, pady=2, sticky=N+W) - self.tabButton1 = Button(self.menuFrame, text="Mail") + self.tabButton1 = Button(self.menuFrame, text="Mail", command=self.openMail) self.tabButton1.grid(row=0, column=1, padx=0, pady=2, sticky=N+W) self.tabButton2 = Button(self.menuFrame, text="Message Flow") self.tabButton2.grid(row=0, column=3, padx=0, pady=2, sticky=N+W) self.menuFrame.grid(row=0, column=0, padx=2, pady=0, sticky=N+W) - self.idFrame = Frame(self.root) self.ourIDLabel = Label(self.idFrame, text="ID: ") @@ -48,11 +45,34 @@ class OnionrGUI: self.syncStatus = Label(self.root, text="Sync Status: 15/100") self.syncStatus.place(relx=1.0, rely=1.0, anchor=S+E) - self.peerCount = Label(self.root, text="Connected Peers: 3") + self.peerCount = Label(self.root, text="Connected Peers: ") self.peerCount.place(relx=0.0, rely=1.0, anchor='sw') self.root.wm_title("Onionr") + threading.Thread(target=self.updateStats) self.root.mainloop() return + def updateStats(self): + #self.core._utils.localCommand() + self.peerCount.config(text='Connected Peers: %s' % ()) + time.sleep(1) + return + def openMail(self): + MailWindow(self) + + +class MailWindow: + def __init__(self, mainGUI): + assert isinstance(mainGUI, OnionrGUI) + self.core = mainGUI.core + self.mailRoot = Toplevel() + + self.inboxFrame = Frame(self.mailRoot) + self.sentboxFrame = Frame(self.mailRoot) + self.composeFrame = Frame(self.mailRoot) + + + self.mailRoot.mainloop() + OnionrGUI() \ No newline at end of file diff --git a/onionr/onionrstorage.py b/onionr/onionrstorage.py index 2a2c44ad..39dfd323 100644 --- a/onionr/onionrstorage.py +++ b/onionr/onionrstorage.py @@ -67,10 +67,12 @@ def getData(coreInst, bHash): assert isinstance(coreInst, core.Core) assert coreInst._utils.validateHash(bHash) + bHash = coreInst._utils.bytesToStr(bHash) + # First check DB for data entry by hash # if no entry, check disk # If no entry in either, raise an exception - retData = '' + retData = None fileLocation = '%s/%s.dat' % (coreInst.blockDataLocation, bHash) if os.path.exists(fileLocation): with open(fileLocation, 'rb') as block: diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py index 95c243f1..18b3e5ca 100644 --- a/onionr/onionrutils.py +++ b/onionr/onionrutils.py @@ -151,7 +151,7 @@ class OnionrUtils: logger.error('Failed to read my address.', error = error) return None - def localCommand(self, command, data='', silent = True): + def localCommand(self, command, data='', silent = True, post=False, postData = {}): ''' Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers. ''' @@ -173,8 +173,12 @@ class OnionrUtils: if data != '': data = '&data=' + urllib.parse.quote_plus(data) payload = 'http://%s:%s/%s%s' % (hostname, config.get('client.client.port'), command, data) + try: - retData = requests.get(payload, headers={'token': config.get('client.webpassword')}).text + if post: + retData = requests.post(payload, data=postData, headers={'token': config.get('client.webpassword')}).text + else: + retData = requests.get(payload, headers={'token': config.get('client.webpassword')}).text except Exception as error: if not silent: logger.error('Failed to make local request (command: %s):%s' % (command, error)) diff --git a/onionr/static-data/default-plugins/flow/main.py b/onionr/static-data/default-plugins/flow/main.py index 8ef0f5f8..09826b79 100644 --- a/onionr/static-data/default-plugins/flow/main.py +++ b/onionr/static-data/default-plugins/flow/main.py @@ -54,7 +54,7 @@ class OnionrFlow: self.flowRunning = False expireTime = self.myCore._utils.getEpoch() + 43200 if len(message) > 0: - self.myCore.insertBlock(message, header='txt', expire=expireTime) + self.myCore.insertBlock(message, header='txt', expire=expireTime, meta={'ch': self.channel}) #insertBL = Block(content = message, type = 'txt', expire=expireTime, core = self.myCore) #insertBL.setMetadata('ch', self.channel) #insertBL.save() @@ -67,10 +67,13 @@ class OnionrFlow: time.sleep(1) try: while self.flowRunning: - for block in Block.getBlocks(type = 'txt', core = self.myCore): + for block in self.myCore.getBlocksByType('txt'): + block = Block(block) if block.getMetadata('ch') != self.channel: + #print('not chan', block.getMetadata('ch')) continue if block.getHash() in self.alreadyOutputed: + #print('already') continue if not self.flowRunning: break @@ -80,7 +83,7 @@ class OnionrFlow: content = self.myCore._utils.escapeAnsi(content.replace('\n', '\\n').replace('\r', '\\r').strip()) logger.info(block.getDate().strftime("%m/%d %H:%M") + ' - ' + logger.colors.reset + content, prompt = False) self.alreadyOutputed.append(block.getHash()) - time.sleep(5) + time.sleep(5) except KeyboardInterrupt: self.flowRunning = False