moved a couple files, work on mail interface, improvements to web + blockapi for block decryption
This commit is contained in:
parent
f0382d24da
commit
557ffa2f4a
12
README.md
12
README.md
@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Anonymous P2P storage network
|
||||||
|
</p>
|
||||||
|
|
||||||
(***pre-alpha quality & experimental, not well tested or easy to use yet***)
|
(***pre-alpha quality & experimental, not well tested or easy to use yet***)
|
||||||
|
|
||||||
[![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.png?v=103)](https://github.com/ellerbrock/open-source-badges/)
|
[![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.png?v=103)](https://github.com/ellerbrock/open-source-badges/)
|
||||||
|
|
||||||
|
|
||||||
Anonymous P2P platform, using Tor & I2P.
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
**The main repository for this software is at https://gitlab.com/beardog/Onionr/**
|
**The main repository for this software is at https://gitlab.com/beardog/Onionr/**
|
||||||
@ -19,9 +20,9 @@ Anonymous P2P platform, using Tor & I2P.
|
|||||||
|
|
||||||
Onionr is a decentralized, peer-to-peer data storage network, designed to be anonymous and resistant to (meta)data analysis and spam.
|
Onionr is a decentralized, peer-to-peer data storage network, designed to be anonymous and resistant to (meta)data analysis and spam.
|
||||||
|
|
||||||
Onionr stores data in independent packages referred to as 'blocks' (not to be confused with a blockchain). The blocks are synced to all other nodes in the network. Blocks and user IDs cannot be easily proven to have been created by particular nodes (only inferred).
|
Onionr stores data in independent packages referred to as 'blocks' (not to be confused with a blockchain). The blocks are synced to all other nodes in the network. Blocks and user IDs cannot be easily proven to have been created by particular nodes (only inferred). Even if there is enough evidence to believe a particular node created a block, nodes still operate behind Tor or I2P and as such are not trivially known to be at a particular IP address.
|
||||||
|
|
||||||
Users are identified by ed25519 public keys, which can be used to sign blocks (optional) or send encrypted data.
|
Users are identified by ed25519 public keys, which can be used to sign blocks or send encrypted data.
|
||||||
|
|
||||||
Onionr can be used for mail, as a social network, instant messenger, file sharing software, or for encrypted group discussion.
|
Onionr can be used for mail, as a social network, instant messenger, file sharing software, or for encrypted group discussion.
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ Onionr can be used for mail, as a social network, instant messenger, file sharin
|
|||||||
* [X] Optional non-encrypted blocks, useful for blog posts or public file sharing
|
* [X] Optional non-encrypted blocks, useful for blog posts or public file sharing
|
||||||
* [X] Easy API system for integration to websites
|
* [X] Easy API system for integration to websites
|
||||||
* [X] Metadata analysis resistance
|
* [X] Metadata analysis resistance
|
||||||
|
* [X] Transport agnosticism
|
||||||
|
|
||||||
**Onionr API and functionality is subject to non-backwards compatible change during pre-alpha development**
|
**Onionr API and functionality is subject to non-backwards compatible change during pre-alpha development**
|
||||||
|
|
||||||
|
@ -139,8 +139,8 @@ class PublicAPI:
|
|||||||
if clientAPI._utils.validateHash(data):
|
if clientAPI._utils.validateHash(data):
|
||||||
if data not in self.hideBlocks:
|
if data not in self.hideBlocks:
|
||||||
if data in clientAPI._core.getBlockList():
|
if data in clientAPI._core.getBlockList():
|
||||||
block = Block(hash=data.encode(), core=clientAPI._core)
|
block = self.clientAPI.getBlockData(data).encode()
|
||||||
resp = base64.b64encode(block.getRaw().encode()).decode()
|
resp = base64.b64encode(block).decode()
|
||||||
if len(resp) == 0:
|
if len(resp) == 0:
|
||||||
abort(404)
|
abort(404)
|
||||||
resp = ""
|
resp = ""
|
||||||
@ -310,7 +310,7 @@ class API:
|
|||||||
|
|
||||||
@app.route('/mail/<path:path>', endpoint='mail')
|
@app.route('/mail/<path:path>', endpoint='mail')
|
||||||
def loadMail(path):
|
def loadMail(path):
|
||||||
return send_from_directory('static-data/www/mail/', '')
|
return send_from_directory('static-data/www/mail/', path)
|
||||||
@app.route('/mail/', endpoint='mailindex')
|
@app.route('/mail/', endpoint='mailindex')
|
||||||
def loadMailIndex():
|
def loadMailIndex():
|
||||||
return send_from_directory('static-data/www/mail/', 'index.html')
|
return send_from_directory('static-data/www/mail/', 'index.html')
|
||||||
@ -358,7 +358,7 @@ class API:
|
|||||||
return Response(','.join(blocks))
|
return Response(','.join(blocks))
|
||||||
|
|
||||||
@app.route('/gethtmlsafeblockdata/<name>')
|
@app.route('/gethtmlsafeblockdata/<name>')
|
||||||
def getData(name):
|
def getSafeData(name):
|
||||||
resp = ''
|
resp = ''
|
||||||
if self._core._utils.validateHash(name):
|
if self._core._utils.validateHash(name):
|
||||||
try:
|
try:
|
||||||
@ -369,6 +369,21 @@ class API:
|
|||||||
abort(404)
|
abort(404)
|
||||||
return Response(resp)
|
return Response(resp)
|
||||||
|
|
||||||
|
@app.route('/getblockdata/<name>')
|
||||||
|
def getData(name):
|
||||||
|
resp = ""
|
||||||
|
if self._core._utils.validateHash(name):
|
||||||
|
if name in self._core.getBlockList():
|
||||||
|
try:
|
||||||
|
resp = self.getBlockData(name, decrypt=True)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
return Response(resp)
|
||||||
|
|
||||||
@app.route('/site/<name>', endpoint='site')
|
@app.route('/site/<name>', endpoint='site')
|
||||||
def site(name):
|
def site(name):
|
||||||
bHash = name
|
bHash = name
|
||||||
@ -453,3 +468,18 @@ class API:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Don't error on race condition with startup
|
# Don't error on race condition with startup
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def getBlockData(self, bHash, decrypt=False):
|
||||||
|
bl = Block(bHash, core=self._core)
|
||||||
|
if decrypt:
|
||||||
|
bl.decrypt()
|
||||||
|
if bl.isEncrypted and not bl.decrypted:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
retData = {'meta':bl.bheader, 'metadata': bl.bmetadata, 'content': bl.bcontent}
|
||||||
|
for x in list(retData.keys()):
|
||||||
|
try:
|
||||||
|
retData[x] = retData[x].decode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return json.dumps(retData)
|
@ -20,9 +20,10 @@
|
|||||||
import sqlite3, os, sys, time, math, base64, tarfile, nacl, logger, json, netcontroller, math, config, uuid
|
import sqlite3, os, sys, time, math, base64, tarfile, nacl, logger, json, netcontroller, math, config, uuid
|
||||||
from onionrblockapi import Block
|
from onionrblockapi import Block
|
||||||
|
|
||||||
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions, onionrvalues
|
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions
|
||||||
import onionrblacklist, onionrusers
|
import onionrblacklist, onionrusers
|
||||||
import dbcreator, onionrstorage, serializeddata
|
import dbcreator, onionrstorage, serializeddata
|
||||||
|
from etc import onionrvalues
|
||||||
|
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6):
|
||||||
try:
|
try:
|
||||||
@ -868,5 +869,4 @@ class Core:
|
|||||||
else:
|
else:
|
||||||
logger.error('Onionr daemon is not running.')
|
logger.error('Onionr daemon is not running.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -25,7 +25,7 @@ class Block:
|
|||||||
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
||||||
blockCache = dict() # should never be accessed directly, look at Block.getCache()
|
blockCache = dict() # should never be accessed directly, look at Block.getCache()
|
||||||
|
|
||||||
def __init__(self, hash = None, core = None, type = None, content = None, expire=None):
|
def __init__(self, hash = None, core = None, type = None, content = None, expire=None, decrypt=False):
|
||||||
# take from arguments
|
# take from arguments
|
||||||
# sometimes people input a bytes object instead of str in `hash`
|
# sometimes people input a bytes object instead of str in `hash`
|
||||||
if (not hash is None) and isinstance(hash, bytes):
|
if (not hash is None) and isinstance(hash, bytes):
|
||||||
@ -51,6 +51,7 @@ class Block:
|
|||||||
self.decrypted = False
|
self.decrypted = False
|
||||||
self.signer = None
|
self.signer = None
|
||||||
self.validSig = False
|
self.validSig = False
|
||||||
|
self.autoDecrypt = decrypt
|
||||||
|
|
||||||
# handle arguments
|
# handle arguments
|
||||||
if self.getCore() is None:
|
if self.getCore() is None:
|
||||||
@ -80,6 +81,7 @@ class Block:
|
|||||||
self.bmetadata = json.loads(bmeta)
|
self.bmetadata = json.loads(bmeta)
|
||||||
self.signature = core._crypto.pubKeyDecrypt(self.signature, anonymous=anonymous, encodedData=encodedData)
|
self.signature = core._crypto.pubKeyDecrypt(self.signature, anonymous=anonymous, encodedData=encodedData)
|
||||||
self.signer = core._crypto.pubKeyDecrypt(self.signer, anonymous=anonymous, encodedData=encodedData)
|
self.signer = core._crypto.pubKeyDecrypt(self.signer, anonymous=anonymous, encodedData=encodedData)
|
||||||
|
self.bheader['signer'] = self.signer.decode()
|
||||||
self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode()
|
self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode()
|
||||||
try:
|
try:
|
||||||
assert self.bmetadata['forwardEnc'] is True
|
assert self.bmetadata['forwardEnc'] is True
|
||||||
@ -191,6 +193,9 @@ class Block:
|
|||||||
if len(self.getRaw()) <= config.get('allocations.blockCache', 500000):
|
if len(self.getRaw()) <= config.get('allocations.blockCache', 500000):
|
||||||
self.cache()
|
self.cache()
|
||||||
|
|
||||||
|
if self.autoDecrypt:
|
||||||
|
self.decrypt()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Failed to parse block %s.' % self.getHash(), error = e, timestamp = False)
|
logger.error('Failed to parse block %s.' % self.getHash(), error = e, timestamp = False)
|
||||||
|
@ -24,7 +24,8 @@ from onionrblockapi import Block
|
|||||||
import onionrexceptions
|
import onionrexceptions
|
||||||
from onionr import API_VERSION
|
from onionr import API_VERSION
|
||||||
import onionrevents
|
import onionrevents
|
||||||
import pgpwords, onionrusers, storagecounter
|
import onionrusers, storagecounter
|
||||||
|
from etc import pgpwords
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6):
|
||||||
try:
|
try:
|
||||||
import sha3
|
import sha3
|
||||||
|
@ -1 +1 @@
|
|||||||
svlegnabtuh3dq6ncmzqmpnxzik5mita5x22up4tai2ekngzcgqbnbqd.onion
|
dd3llxdp5q6ak3zmmicoy3jnodmroouv2xr7whkygiwp3rl7nf23gdad.onion
|
@ -12,10 +12,12 @@
|
|||||||
<div id="infoOverlay" class='overlay'>
|
<div id="infoOverlay" class='overlay'>
|
||||||
</div>
|
</div>
|
||||||
<img class='logo' src='/shared/onionr-icon.png' alt='onionr logo'>
|
<img class='logo' src='/shared/onionr-icon.png' alt='onionr logo'>
|
||||||
<span class='logoText'>Onionr Web Mail</span>
|
<span class='logoText'>Onionr Mail</span>
|
||||||
<div class='content'>
|
<div class='content'>
|
||||||
|
<button class='refresh'>Refresh</button>
|
||||||
|
<div id='threads'></div>
|
||||||
</div>
|
</div>
|
||||||
<script src='/shared/misc.js'></script>
|
<script src='/shared/misc.js'></script>
|
||||||
|
<script src='/mail/mail.js'></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -0,0 +1,28 @@
|
|||||||
|
pms = ''
|
||||||
|
threadPart = document.getElementById('threads')
|
||||||
|
function getInbox(){
|
||||||
|
for(var i = 0; i < pms.length; i++) {
|
||||||
|
fetch('/getblockdata/' + pms[i], {
|
||||||
|
headers: {
|
||||||
|
"token": webpass
|
||||||
|
}})
|
||||||
|
.then((resp) => resp.json()) // Transform the data into json
|
||||||
|
.then(function(resp) {
|
||||||
|
var entry = document.createElement('div')
|
||||||
|
entry.innerHTML = resp['meta']['time'] + ' - ' + resp['meta']['signer']
|
||||||
|
threadPart.appendChild(entry)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/getblocksbytype/pm', {
|
||||||
|
headers: {
|
||||||
|
"token": webpass
|
||||||
|
}})
|
||||||
|
.then((resp) => resp.text()) // Transform the data into json
|
||||||
|
.then(function(data) {
|
||||||
|
pms = data.split(',')
|
||||||
|
getInbox()
|
||||||
|
})
|
||||||
|
|
@ -18,6 +18,7 @@
|
|||||||
<span class='logoText'>Onionr Web Control Panel</span>
|
<span class='logoText'>Onionr Web Control Panel</span>
|
||||||
<div class='content'>
|
<div class='content'>
|
||||||
<button id='shutdownNode'>Shutdown Node</button> <button id='refreshStats'>Refresh Stats</button>
|
<button id='shutdownNode'>Shutdown Node</button> <button id='refreshStats'>Refresh Stats</button>
|
||||||
|
<br><br><a class='idLink' href='/mail/'>Mail</a>
|
||||||
<h2>Stats</h2>
|
<h2>Stats</h2>
|
||||||
<p>Uptime: <span id='uptime'></span></p>
|
<p>Uptime: <span id='uptime'></span></p>
|
||||||
<p>Stored Blocks: <span id='storedBlocks'></span></p>
|
<p>Stored Blocks: <span id='storedBlocks'></span></p>
|
||||||
|
@ -5,7 +5,7 @@ if (typeof webpass == "undefined"){
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
localStorage['webpass'] = webpass
|
localStorage['webpass'] = webpass
|
||||||
document.location.hash = ''
|
//document.location.hash = ''
|
||||||
}
|
}
|
||||||
if (typeof webpass == "undefined" || webpass == ""){
|
if (typeof webpass == "undefined" || webpass == ""){
|
||||||
alert('Web password was not found in memory or URL')
|
alert('Web password was not found in memory or URL')
|
||||||
@ -28,3 +28,17 @@ function overlay(overlayID) {
|
|||||||
el = document.getElementById(overlayID)
|
el = document.getElementById(overlayID)
|
||||||
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible"
|
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var passLinks = document.getElementsByClassName("idLink")
|
||||||
|
for(var i = 0; i < passLinks.length; i++) {
|
||||||
|
passLinks[i].href += '#' + webpass
|
||||||
|
}
|
||||||
|
|
||||||
|
var refreshLinks = document.getElementsByClassName("refresh")
|
||||||
|
|
||||||
|
for(var i = 0; i < refreshLinks.length; i++) {
|
||||||
|
//Can't use .reload because of webpass
|
||||||
|
refreshLinks[i].onclick = function(){
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user