diff --git a/Makefile b/Makefile
index 472ffc2d..c51fc72b 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@
setup:
sudo pip3 install -r requirements.txt
+ -@cd onionr/static-data/ui/; ./compile.py
install:
sudo rm -rf /usr/share/onionr/
diff --git a/docs/whitepaper.md b/docs/whitepaper.md
index 6309f4f4..789f8e47 100644
--- a/docs/whitepaper.md
+++ b/docs/whitepaper.md
@@ -16,9 +16,9 @@ To prevent censorship or loss of information, these measures must be in place:
* Anonymization of users by default
* The Inability to violently coerce human users (personal threats/"doxxing", or totalitarian regime censorship)
-* Economic availability. A system should not rely on a single device to be constantly online, and should not be overtly expensive to use. The majority of people in the world own cell phones, but comparatively few own personal computers, particularly in developing countries.
+* Economic availability. A system should not rely on a single device to be constantly online, and should not be overly expensive to use. The majority of people in the world own cell phones, but comparatively few own personal computers, particularly in developing countries.
-There are many great projects that tackle decentralization and privacy issues, but there are none which tackle all of the above issue. Some of the existing networks have also not worked well in practice, or are more complicated than they need to be.
+There are many great projects that tackle decentralization and privacy issues, but there are none which tackle all of the above issues. Some of the existing networks have also not worked well in practice, or are more complicated than they need to be.
# Onionr Design Goals
diff --git a/onionr/api.py b/onionr/api.py
index 43e7cabc..705a4781 100755
--- a/onionr/api.py
+++ b/onionr/api.py
@@ -30,6 +30,9 @@ class API:
'''
Main HTTP API (Flask)
'''
+
+ callbacks = {'public' : {}, 'private' : {}, 'ui' : {}}
+
def validateToken(self, token):
'''
Validate that the client token matches the given token
@@ -126,7 +129,7 @@ class API:
if not hmac.compare_digest(timingToken, self.timeBypassToken):
if elapsed < self._privateDelayTime:
time.sleep(self._privateDelayTime - elapsed)
- return send_from_directory('static-data/ui/', path)
+ return send_from_directory('static-data/ui/dist/', path)
@app.route('/client/')
def private_handler():
@@ -164,6 +167,44 @@ class API:
self.mimeType = 'text/html'
response = siteData.split(b'-', 2)[-1]
resp = Response(response)
+ elif action == "insertBlock":
+ response = {'success' : False, 'reason' : 'An unknown error occurred'}
+
+ try:
+ decoded = json.loads(data)
+
+ block = Block()
+
+ sign = False
+
+ for key in decoded:
+ val = decoded[key]
+
+ key = key.lower()
+
+ if key == 'type':
+ block.setType(val)
+ elif key in ['body', 'content']:
+ block.setContent(val)
+ elif key == 'parent':
+ block.setParent(val)
+ elif key == 'sign':
+ sign = (str(val).lower() == 'true')
+
+ hash = block.save(sign = sign)
+
+ if not hash is False:
+ response['success'] = true
+ response['hash'] = hash
+ response['reason'] = 'Successfully wrote block to file'
+ else:
+ response['reason'] = 'Faield to save the block'
+ except Exception as e:
+ logger.debug('insertBlock api request failed', error = e)
+
+ resp = Response(json.dumps(response))
+ elif action in callbacks['private']:
+ resp = Response(str(getCallback(action, scope = 'private')(request)))
else:
resp = Response('(O_o) Dude what? (invalid command)')
endTime = math.floor(time.time())
@@ -258,6 +299,8 @@ class API:
peers = self._core.listPeers(getPow=True)
response = ','.join(peers)
resp = Response(response)
+ elif action in callbacks['public']:
+ resp = Response(str(getCallback(action, scope = 'public')(request)))
else:
resp = Response("")
@@ -328,3 +371,31 @@ class API:
# we exit rather than abort to avoid fingerprinting
logger.debug('Avoiding fingerprinting, exiting...')
sys.exit(1)
+
+ def setCallback(action, callback, scope = 'public'):
+ if not scope in callbacks:
+ return False
+
+ callbacks[scope][action] = callback
+
+ return True
+
+ def removeCallback(action, scope = 'public'):
+ if (not scope in callbacks) or (not action in callbacks[scope]):
+ return False
+
+ del callbacks[scope][action]
+
+ return True
+
+ def getCallback(action, scope = 'public'):
+ if (not scope in callbacks) or (not action in callbacks[scope]):
+ return None
+
+ return callbacks[scope][action]
+
+ def getCallbacks(scope = None):
+ if (not scope is None) and (scope in callbacks):
+ return callbacks[scope]
+
+ return callbacks
diff --git a/onionr/communicator2.py b/onionr/communicator2.py
index b3734cbc..b420fcba 100755
--- a/onionr/communicator2.py
+++ b/onionr/communicator2.py
@@ -36,7 +36,7 @@ class OnionrCommunicatorDaemon:
# intalize NIST beacon salt and time
self.nistSaltTimestamp = 0
self.powSalt = 0
-
+
self.blockToUpload = ''
# loop time.sleep delay in seconds
@@ -309,7 +309,7 @@ class OnionrCommunicatorDaemon:
logger.info(i)
def peerAction(self, peer, action, data=''):
- '''Perform a get request to a peer'''
+ '''Perform a get request to a peer'''
if len(peer) == 0:
return False
logger.info('Performing ' + action + ' with ' + peer + ' on port ' + str(self.proxyPort))
diff --git a/onionr/onionr.py b/onionr/onionr.py
index 11db4351..ec3ec468 100755
--- a/onionr/onionr.py
+++ b/onionr/onionr.py
@@ -91,8 +91,6 @@ class Onionr:
self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils(self.onionrCore)
- self.userOS = platform.system()
-
# Handle commands
self.debug = False # Whole application debugging
@@ -258,7 +256,7 @@ class Onionr:
def getWebPassword(self):
return config.get('client.hmac')
-
+
def printWebPassword(self):
print(self.getWebPassword())
@@ -542,7 +540,7 @@ class Onionr:
subprocess.Popen([communicatorDaemon, "run", str(net.socksPort)])
logger.debug('Started communicator')
events.event('daemon_start', onionr = self)
- api.API(self.debug)
+ self.api = api.API(self.debug)
return
diff --git a/onionr/onionrblockapi.py b/onionr/onionrblockapi.py
index 695d41ab..a49210d3 100644
--- a/onionr/onionrblockapi.py
+++ b/onionr/onionrblockapi.py
@@ -38,7 +38,6 @@ class Block:
self.btype = type
self.bcontent = content
-
# initialize variables
self.valid = True
self.raw = None
@@ -71,8 +70,10 @@ class Block:
# logic
- def decrypt(self, anonymous=True, encodedData=True):
- '''Decrypt a block, loading decrypted data into their vars'''
+ def decrypt(self, anonymous = True, encodedData = True):
+ '''
+ Decrypt a block, loading decrypted data into their vars
+ '''
if self.decrypted:
return True
retData = False
@@ -100,9 +101,11 @@ class Block:
else:
logger.warn('symmetric decryption is not yet supported by this API')
return retData
-
+
def verifySig(self):
- '''Verify if a block's signature is signed by its claimed signer'''
+ '''
+ Verify if a block's signature is signed by its claimed signer
+ '''
core = self.getCore()
if core._crypto.edVerify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
@@ -227,12 +230,14 @@ class Block:
else:
self.hash = self.getCore().insertBlock(self.getContent(), header = self.getType(), sign = sign)
self.update()
+
return self.getHash()
else:
logger.warn('Not writing block; it is invalid.')
except Exception as e:
logger.error('Failed to save block.', error = e, timestamp = False)
- return False
+
+ return False
# getters
@@ -533,7 +538,7 @@ class Block:
if relevant:
relevant_blocks.append(block)
-
+
if bool(reverse):
relevant_blocks.reverse()
diff --git a/onionr/onionrpluginapi.py b/onionr/onionrpluginapi.py
index bfaf73e8..0120dad7 100644
--- a/onionr/onionrpluginapi.py
+++ b/onionr/onionrpluginapi.py
@@ -130,6 +130,22 @@ class CommandAPI:
def get_commands(self):
return self.pluginapi.get_onionr().getCommands()
+class WebAPI:
+ def __init__(self, pluginapi):
+ self.pluginapi = pluginapi
+
+ def register_callback(self, action, callback, scope = 'public'):
+ return self.pluginapi.get_onionr().api.setCallback(action, callback, scope = scope)
+
+ def unregister_callback(self, action, scope = 'public'):
+ return self.pluginapi.get_onionr().api.removeCallback(action, scope = scope)
+
+ def get_callback(self, action, scope = 'public'):
+ return self.pluginapi.get_onionr().api.getCallback(action, scope= scope)
+
+ def get_callbacks(self, scope = None):
+ return self.pluginapi.get_onionr().api.getCallbacks(scope = scope)
+
class pluginapi:
def __init__(self, onionr, data):
self.onionr = onionr
@@ -142,6 +158,7 @@ class pluginapi:
self.daemon = DaemonAPI(self)
self.plugins = PluginAPI(self)
self.commands = CommandAPI(self)
+ self.web = WebAPI(self)
def get_onionr(self):
return self.onionr
@@ -167,5 +184,8 @@ class pluginapi:
def get_commandapi(self):
return self.commands
+ def get_webapi(self):
+ return self.web
+
def is_development_mode(self):
return self.get_onionr()._developmentMode
diff --git a/onionr/static-data/ui/readme.txt b/onionr/static-data/ui/README.md
similarity index 100%
rename from onionr/static-data/ui/readme.txt
rename to onionr/static-data/ui/README.md
diff --git a/onionr/static-data/ui/common/footer.html b/onionr/static-data/ui/common/footer.html
new file mode 100644
index 00000000..6b5cfb06
--- /dev/null
+++ b/onionr/static-data/ui/common/footer.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/onionr/static-data/ui/common/header.html b/onionr/static-data/ui/common/header.html
new file mode 100644
index 00000000..2a2b4f56
--- /dev/null
+++ b/onionr/static-data/ui/common/header.html
@@ -0,0 +1,30 @@
+