merge refactorinsert into travis' ui work
This commit is contained in:
commit
d70afbf92b
115 changed files with 2280 additions and 1556 deletions
|
@ -23,12 +23,12 @@ from gevent import Timeout
|
|||
import flask
|
||||
from flask import request, Response, abort, send_from_directory
|
||||
import core
|
||||
from onionrblockapi import Block
|
||||
import onionrutils, onionrexceptions, onionrcrypto, blockimporter, onionrevents as events, logger, config
|
||||
import onionrexceptions, onionrcrypto, blockimporter, onionrevents as events, logger, config, onionrblockapi
|
||||
import httpapi
|
||||
from httpapi import friendsapi, profilesapi, configapi, miscpublicapi
|
||||
from onionrservices import httpheaders
|
||||
import onionr
|
||||
from onionrutils import bytesconverter, stringvalidators, epoch, mnemonickeys
|
||||
|
||||
config.reload()
|
||||
class FDSafeHandler(WSGIHandler):
|
||||
|
@ -99,7 +99,7 @@ class PublicAPI:
|
|||
resp = httpheaders.set_default_onionr_http_headers(resp)
|
||||
# Network API version
|
||||
resp.headers['X-API'] = onionr.API_VERSION
|
||||
self.lastRequest = clientAPI._core._utils.getRoundedEpoch(roundS=5)
|
||||
self.lastRequest = epoch.get_rounded_epoch(roundS=5)
|
||||
return resp
|
||||
|
||||
@app.route('/')
|
||||
|
@ -178,9 +178,8 @@ class API:
|
|||
|
||||
self.debug = debug
|
||||
self._core = onionrInst.onionrCore
|
||||
self.startTime = self._core._utils.getEpoch()
|
||||
self.startTime = epoch.get_epoch()
|
||||
self._crypto = onionrcrypto.OnionrCrypto(self._core)
|
||||
self._utils = onionrutils.OnionrUtils(self._core)
|
||||
app = flask.Flask(__name__)
|
||||
bindPort = int(config.get('client.client.port', 59496))
|
||||
self.bindPort = bindPort
|
||||
|
@ -335,9 +334,9 @@ class API:
|
|||
@app.route('/getblockbody/<name>')
|
||||
def getBlockBodyData(name):
|
||||
resp = ''
|
||||
if self._core._utils.validateHash(name):
|
||||
if stringvalidators.validate_hash(name):
|
||||
try:
|
||||
resp = Block(name, decrypt=True).bcontent
|
||||
resp = onionrblockapi.Block(name, decrypt=True).bcontent
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
|
@ -347,7 +346,7 @@ class API:
|
|||
@app.route('/getblockdata/<name>')
|
||||
def getData(name):
|
||||
resp = ""
|
||||
if self._core._utils.validateHash(name):
|
||||
if stringvalidators.validate_hash(name):
|
||||
if name in self._core.getBlockList():
|
||||
try:
|
||||
resp = self.getBlockData(name, decrypt=True)
|
||||
|
@ -372,9 +371,9 @@ class API:
|
|||
def site(name):
|
||||
bHash = name
|
||||
resp = 'Not Found'
|
||||
if self._core._utils.validateHash(bHash):
|
||||
if stringvalidators.validate_hash(bHash):
|
||||
try:
|
||||
resp = Block(bHash).bcontent
|
||||
resp = onionrblockapi.Block(bHash).bcontent
|
||||
except onionrexceptions.NoDataAvailable:
|
||||
abort(404)
|
||||
except TypeError:
|
||||
|
@ -433,7 +432,7 @@ class API:
|
|||
|
||||
@app.route('/getHumanReadable/<name>')
|
||||
def getHumanReadable(name):
|
||||
return Response(self._core._utils.getHumanReadableID(name))
|
||||
return Response(mnemonickeys.get_human_readable_ID(name))
|
||||
|
||||
@app.route('/insertblock', methods=['POST'])
|
||||
def insertBlock():
|
||||
|
@ -498,14 +497,14 @@ class API:
|
|||
def getUptime(self):
|
||||
while True:
|
||||
try:
|
||||
return self._utils.getEpoch() - self.startTime
|
||||
return epoch.get_epoch() - self.startTime
|
||||
except (AttributeError, NameError):
|
||||
# Don't error on race condition with startup
|
||||
pass
|
||||
|
||||
def getBlockData(self, bHash, decrypt=False, raw=False, headerOnly=False):
|
||||
assert self._core._utils.validateHash(bHash)
|
||||
bl = Block(bHash, core=self._core)
|
||||
assert stringvalidators.validate_hash(bHash)
|
||||
bl = onionrblockapi.Block(bHash, core=self._core)
|
||||
if decrypt:
|
||||
bl.decrypt()
|
||||
if bl.isEncrypted and not bl.decrypted:
|
||||
|
@ -521,8 +520,8 @@ class API:
|
|||
pass
|
||||
else:
|
||||
validSig = False
|
||||
signer = self._core._utils.bytesToStr(bl.signer)
|
||||
if bl.isSigned() and self._core._utils.validatePubKey(signer) and bl.isSigner(signer):
|
||||
signer = bytesconverter.bytes_to_str(bl.signer)
|
||||
if bl.isSigned() and stringvalidators.validate_pub_key(signer) and bl.isSigner(signer):
|
||||
validSig = True
|
||||
bl.bheader['validSig'] = validSig
|
||||
bl.bheader['meta'] = ''
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import core, onionrexceptions, logger
|
||||
from onionrutils import validatemetadata, blockmetadata
|
||||
def importBlockFromData(content, coreInst):
|
||||
retData = False
|
||||
|
||||
|
@ -34,17 +35,17 @@ def importBlockFromData(content, coreInst):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
metas = coreInst._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||
metas = blockmetadata.get_block_metadata_from_data(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||
metadata = metas[0]
|
||||
if coreInst._utils.validateMetadata(metadata, metas[2]): # check if metadata is valid
|
||||
if validatemetadata.validate_metadata(coreInst, metadata, metas[2]): # check if metadata is valid
|
||||
if coreInst._crypto.verifyPow(content): # check if POW is enough/correct
|
||||
logger.info('Block passed proof, saving.')
|
||||
logger.info('Block passed proof, saving.', terminal=True)
|
||||
try:
|
||||
blockHash = coreInst.setData(content)
|
||||
except onionrexceptions.DiskAllocationReached:
|
||||
pass
|
||||
else:
|
||||
coreInst.addToBlockDB(blockHash, dataSaved=True)
|
||||
coreInst._utils.processBlockMetadata(blockHash) # caches block metadata values to block database
|
||||
blockmetadata.process_block_metadata(coreInst, blockHash) # caches block metadata values to block database
|
||||
retData = True
|
||||
return retData
|
|
@ -27,6 +27,7 @@ from communicatorutils import downloadblocks, lookupblocks, lookupadders
|
|||
from communicatorutils import servicecreator, connectnewpeers, uploadblocks
|
||||
from communicatorutils import daemonqueuehandler, announcenode, deniableinserts
|
||||
from communicatorutils import cooldownpeer, housekeeping, netcheck
|
||||
from onionrutils import localcommand, epoch, basicrequests
|
||||
from etc import humanreadabletime
|
||||
import onionrservices, onionr, onionrproofs
|
||||
|
||||
|
@ -90,7 +91,7 @@ class OnionrCommunicatorDaemon:
|
|||
plugins.reload()
|
||||
|
||||
# time app started running for info/statistics purposes
|
||||
self.startTime = self._core._utils.getEpoch()
|
||||
self.startTime = epoch.get_epoch()
|
||||
|
||||
if developmentMode:
|
||||
OnionrCommunicatorTimers(self, self.heartbeat, 30)
|
||||
|
@ -176,7 +177,7 @@ class OnionrCommunicatorDaemon:
|
|||
self.shutdown = True
|
||||
pass
|
||||
|
||||
logger.info('Goodbye. (Onionr is cleaning up, and will exit)')
|
||||
logger.info('Goodbye. (Onionr is cleaning up, and will exit)', terminal=True)
|
||||
try:
|
||||
self.service_greenlets
|
||||
except AttributeError:
|
||||
|
@ -184,7 +185,7 @@ class OnionrCommunicatorDaemon:
|
|||
else:
|
||||
for server in self.service_greenlets:
|
||||
server.stop()
|
||||
self._core._utils.localCommand('shutdown') # shutdown the api
|
||||
localcommand.local_command(self._core, 'shutdown') # shutdown the api
|
||||
time.sleep(0.5)
|
||||
|
||||
def lookupAdders(self):
|
||||
|
@ -252,7 +253,7 @@ class OnionrCommunicatorDaemon:
|
|||
break
|
||||
else:
|
||||
if len(self.onlinePeers) == 0:
|
||||
logger.debug('Couldn\'t connect to any peers.' + (' Last node seen %s ago.' % humanreadabletime.human_readable_time(time.time() - self.lastNodeSeen) if not self.lastNodeSeen is None else ''))
|
||||
logger.debug('Couldn\'t connect to any peers.' + (' Last node seen %s ago.' % humanreadabletime.human_readable_time(time.time() - self.lastNodeSeen) if not self.lastNodeSeen is None else ''), terminal=True)
|
||||
else:
|
||||
self.lastNodeSeen = time.time()
|
||||
self.decrementThreadCount('getOnlinePeers')
|
||||
|
@ -293,12 +294,12 @@ class OnionrCommunicatorDaemon:
|
|||
def printOnlinePeers(self):
|
||||
'''logs online peer list'''
|
||||
if len(self.onlinePeers) == 0:
|
||||
logger.warn('No online peers')
|
||||
logger.warn('No online peers', terminal=True)
|
||||
else:
|
||||
logger.info('Online peers:')
|
||||
logger.info('Online peers:', terminal=True)
|
||||
for i in self.onlinePeers:
|
||||
score = str(self.getPeerProfileInstance(i).score)
|
||||
logger.info(i + ', score: ' + score)
|
||||
logger.info(i + ', score: ' + score, terminal=True)
|
||||
|
||||
def peerAction(self, peer, action, data='', returnHeaders=False):
|
||||
'''Perform a get request to a peer'''
|
||||
|
@ -309,20 +310,21 @@ class OnionrCommunicatorDaemon:
|
|||
if len(data) > 0:
|
||||
url += '&data=' + data
|
||||
|
||||
self._core.setAddressInfo(peer, 'lastConnectAttempt', self._core._utils.getEpoch()) # mark the time we're trying to request this peer
|
||||
self._core.setAddressInfo(peer, 'lastConnectAttempt', epoch.get_epoch()) # mark the time we're trying to request this peer
|
||||
|
||||
retData = self._core._utils.doGetRequest(url, port=self.proxyPort)
|
||||
retData = basicrequests.do_get_request(self._core, url, port=self.proxyPort)
|
||||
# if request failed, (error), mark peer offline
|
||||
if retData == False:
|
||||
try:
|
||||
self.getPeerProfileInstance(peer).addScore(-10)
|
||||
self.removeOnlinePeer(peer)
|
||||
if action != 'ping':
|
||||
if action != 'ping' and not self.shutdown:
|
||||
logger.warn('Lost connection to ' + peer, terminal=True)
|
||||
self.getOnlinePeers() # Will only add a new peer to pool if needed
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self._core.setAddressInfo(peer, 'lastConnect', self._core._utils.getEpoch())
|
||||
self._core.setAddressInfo(peer, 'lastConnect', epoch.get_epoch())
|
||||
self.getPeerProfileInstance(peer).addScore(1)
|
||||
return retData # If returnHeaders, returns tuple of data, headers. if not, just data string
|
||||
|
||||
|
@ -339,7 +341,7 @@ class OnionrCommunicatorDaemon:
|
|||
return retData
|
||||
|
||||
def getUptime(self):
|
||||
return self._core._utils.getEpoch() - self.startTime
|
||||
return epoch.get_epoch() - self.startTime
|
||||
|
||||
def heartbeat(self):
|
||||
'''Show a heartbeat debug message'''
|
||||
|
@ -359,19 +361,19 @@ class OnionrCommunicatorDaemon:
|
|||
def announce(self, peer):
|
||||
'''Announce to peers our address'''
|
||||
if announcenode.announce_node(self) == False:
|
||||
logger.warn('Could not introduce node.')
|
||||
logger.warn('Could not introduce node.', terminal=True)
|
||||
|
||||
def detectAPICrash(self):
|
||||
'''exit if the api server crashes/stops'''
|
||||
if self._core._utils.localCommand('ping', silent=False) not in ('pong', 'pong!'):
|
||||
if localcommand.local_command(self._core, 'ping', silent=False) not in ('pong', 'pong!'):
|
||||
for i in range(300):
|
||||
if self._core._utils.localCommand('ping') in ('pong', 'pong!') or self.shutdown:
|
||||
if localcommand.local_command(self._core, 'ping') in ('pong', 'pong!') or self.shutdown:
|
||||
break # break for loop
|
||||
time.sleep(1)
|
||||
else:
|
||||
# This executes if the api is NOT detected to be running
|
||||
events.event('daemon_crash', onionr = self._core.onionrInst, data = {})
|
||||
logger.error('Daemon detected API crash (or otherwise unable to reach API after long time), stopping...')
|
||||
logger.fatal('Daemon detected API crash (or otherwise unable to reach API after long time), stopping...', terminal=True)
|
||||
self.shutdown = True
|
||||
self.decrementThreadCount('detectAPICrash')
|
||||
|
||||
|
@ -388,5 +390,4 @@ def run_file_exists(daemon):
|
|||
if os.path.isfile(daemon._core.dataDir + '.runcheck'):
|
||||
os.remove(daemon._core.dataDir + '.runcheck')
|
||||
return True
|
||||
|
||||
return False
|
|
@ -20,6 +20,7 @@
|
|||
import base64
|
||||
import onionrproofs, logger
|
||||
from etc import onionrvalues
|
||||
from onionrutils import basicrequests, bytesconverter
|
||||
|
||||
def announce_node(daemon):
|
||||
'''Announce our node to our peers'''
|
||||
|
@ -52,7 +53,7 @@ def announce_node(daemon):
|
|||
combinedNodes = ourID + peer
|
||||
if ourID != 1:
|
||||
#TODO: Extend existingRand for i2p
|
||||
existingRand = daemon._core._utils.bytesToStr(daemon._core.getAddressInfo(peer, 'powValue'))
|
||||
existingRand = bytesconverter.bytes_to_str(daemon._core.getAddressInfo(peer, 'powValue'))
|
||||
# Reset existingRand if it no longer meets the minimum POW
|
||||
if type(existingRand) is type(None) or not existingRand.endswith('0' * ov.announce_pow):
|
||||
existingRand = ''
|
||||
|
@ -75,8 +76,8 @@ def announce_node(daemon):
|
|||
daemon.announceCache[peer] = data['random']
|
||||
if not announceFail:
|
||||
logger.info('Announcing node to ' + url)
|
||||
if daemon._core._utils.doPostRequest(url, data) == 'Success':
|
||||
logger.info('Successfully introduced node to ' + peer)
|
||||
if basicrequests.do_post_request(daemon._core, url, data) == 'Success':
|
||||
logger.info('Successfully introduced node to ' + peer, terminal=True)
|
||||
retData = True
|
||||
daemon._core.setAddressInfo(peer, 'introduced', 1)
|
||||
daemon._core.setAddressInfo(peer, 'powValue', data['random'])
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import time, sys
|
||||
import onionrexceptions, logger, onionrpeers
|
||||
from utils import networkmerger
|
||||
from onionrutils import stringvalidators, epoch
|
||||
# secrets module was added into standard lib in 3.6+
|
||||
if sys.version_info[0] == 3 and sys.version_info[1] < 6:
|
||||
from dependencies import secrets
|
||||
|
@ -30,7 +31,7 @@ def connect_new_peer_to_communicator(comm_inst, peer='', useBootstrap=False):
|
|||
retData = False
|
||||
tried = comm_inst.offlinePeers
|
||||
if peer != '':
|
||||
if comm_inst._core._utils.validateID(peer):
|
||||
if stringvalidators.validate_transport(peer):
|
||||
peerList = [peer]
|
||||
else:
|
||||
raise onionrexceptions.InvalidAddress('Will not attempt connection test to invalid address')
|
||||
|
@ -72,9 +73,9 @@ def connect_new_peer_to_communicator(comm_inst, peer='', useBootstrap=False):
|
|||
# Add a peer to our list if it isn't already since it successfully connected
|
||||
networkmerger.mergeAdders(address, comm_inst._core)
|
||||
if address not in comm_inst.onlinePeers:
|
||||
logger.info('Connected to ' + address)
|
||||
logger.info('Connected to ' + address, terminal=True)
|
||||
comm_inst.onlinePeers.append(address)
|
||||
comm_inst.connectTimes[address] = comm_inst._core._utils.getEpoch()
|
||||
comm_inst.connectTimes[address] = epoch.get_epoch()
|
||||
retData = address
|
||||
|
||||
# add peer to profile list if they're not in it
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
from onionrutils import epoch
|
||||
def cooldown_peer(comm_inst):
|
||||
'''Randomly add an online peer to cooldown, so we can connect a new one'''
|
||||
onlinePeerAmount = len(comm_inst.onlinePeers)
|
||||
|
@ -28,7 +29,7 @@ def cooldown_peer(comm_inst):
|
|||
# Remove peers from cooldown that have been there long enough
|
||||
tempCooldown = dict(comm_inst.cooldownPeer)
|
||||
for peer in tempCooldown:
|
||||
if (comm_inst._core._utils.getEpoch() - tempCooldown[peer]) >= cooldownTime:
|
||||
if (epoch.get_epoch() - tempCooldown[peer]) >= cooldownTime:
|
||||
del comm_inst.cooldownPeer[peer]
|
||||
|
||||
# Cool down a peer, if we have max connections alive for long enough
|
||||
|
@ -38,7 +39,7 @@ def cooldown_peer(comm_inst):
|
|||
while finding:
|
||||
try:
|
||||
toCool = min(tempConnectTimes, key=tempConnectTimes.get)
|
||||
if (comm_inst._core._utils.getEpoch() - tempConnectTimes[toCool]) < minTime:
|
||||
if (epoch.get_epoch() - tempConnectTimes[toCool]) < minTime:
|
||||
del tempConnectTimes[toCool]
|
||||
else:
|
||||
finding = False
|
||||
|
@ -46,6 +47,6 @@ def cooldown_peer(comm_inst):
|
|||
break
|
||||
else:
|
||||
comm_inst.removeOnlinePeer(toCool)
|
||||
comm_inst.cooldownPeer[toCool] = comm_inst._core._utils.getEpoch()
|
||||
comm_inst.cooldownPeer[toCool] = epoch.get_epoch()
|
||||
|
||||
comm_inst.decrementThreadCount('cooldown_peer')
|
|
@ -19,6 +19,7 @@
|
|||
'''
|
||||
import logger
|
||||
import onionrevents as events
|
||||
from onionrutils import localcommand
|
||||
def handle_daemon_commands(comm_inst):
|
||||
cmd = comm_inst._core.daemonQueue()
|
||||
response = ''
|
||||
|
@ -39,7 +40,7 @@ def handle_daemon_commands(comm_inst):
|
|||
if response == '':
|
||||
response = 'none'
|
||||
elif cmd[0] == 'localCommand':
|
||||
response = comm_inst._core._utils.localCommand(cmd[1])
|
||||
response = localcommand.local_command(comm_inst._core, cmd[1])
|
||||
elif cmd[0] == 'pex':
|
||||
for i in comm_inst.timers:
|
||||
if i.timerFunction.__name__ == 'lookupAdders':
|
||||
|
@ -49,7 +50,7 @@ def handle_daemon_commands(comm_inst):
|
|||
|
||||
if cmd[0] not in ('', None):
|
||||
if response != '':
|
||||
comm_inst._core._utils.localCommand('queueResponseAdd/' + cmd[4], post=True, postData={'data': response})
|
||||
localcommand.local_command(comm_inst._core, 'queueResponseAdd/' + cmd[4], post=True, postData={'data': response})
|
||||
response = ''
|
||||
|
||||
comm_inst.decrementThreadCount('daemonCommands')
|
|
@ -27,5 +27,5 @@ def insert_deniable_block(comm_inst):
|
|||
# This assumes on the libsodium primitives to have key-privacy
|
||||
fakePeer = onionrvalues.DENIABLE_PEER_ADDRESS
|
||||
data = secrets.token_hex(secrets.randbelow(1024) + 1)
|
||||
comm_inst._core.insertBlock(data, header='pm', encryptType='asym', asymPeer=fakePeer, meta={'subject': 'foo'})
|
||||
comm_inst._core.insertBlock(data, header='pm', encryptType='asym', asymPeer=fakePeer, disableForward=True, meta={'subject': 'foo'})
|
||||
comm_inst.decrementThreadCount('insert_deniable_block')
|
|
@ -19,6 +19,7 @@
|
|||
'''
|
||||
import communicator, onionrexceptions
|
||||
import logger, onionrpeers
|
||||
from onionrutils import blockmetadata, stringvalidators, validatemetadata
|
||||
|
||||
def download_blocks_from_communicator(comm_inst):
|
||||
assert isinstance(comm_inst, communicator.OnionrCommunicatorDaemon)
|
||||
|
@ -47,7 +48,7 @@ def download_blocks_from_communicator(comm_inst):
|
|||
continue
|
||||
if comm_inst._core._blacklist.inBlacklist(blockHash):
|
||||
continue
|
||||
if comm_inst._core._utils.storageCounter.isFull():
|
||||
if comm_inst._core.storage_counter.isFull():
|
||||
break
|
||||
comm_inst.currentDownloading.append(blockHash) # So we can avoid concurrent downloading in other threads of same block
|
||||
if len(blockPeers) == 0:
|
||||
|
@ -72,9 +73,9 @@ def download_blocks_from_communicator(comm_inst):
|
|||
pass
|
||||
if realHash == blockHash:
|
||||
content = content.decode() # decode here because sha3Hash needs bytes above
|
||||
metas = comm_inst._core._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||
metas = blockmetadata.get_block_metadata_from_data(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||
metadata = metas[0]
|
||||
if comm_inst._core._utils.validateMetadata(metadata, metas[2]): # check if metadata is valid, and verify nonce
|
||||
if validatemetadata.validate_metadata(comm_inst._core, metadata, metas[2]): # check if metadata is valid, and verify nonce
|
||||
if comm_inst._core._crypto.verifyPow(content): # check if POW is enough/correct
|
||||
logger.info('Attempting to save block %s...' % blockHash[:12])
|
||||
try:
|
||||
|
@ -84,7 +85,7 @@ def download_blocks_from_communicator(comm_inst):
|
|||
removeFromQueue = False
|
||||
else:
|
||||
comm_inst._core.addToBlockDB(blockHash, dataSaved=True)
|
||||
comm_inst._core._utils.processBlockMetadata(blockHash) # caches block metadata values to block database
|
||||
blockmetadata.process_block_metadata(comm_inst._core, blockHash) # caches block metadata values to block database
|
||||
else:
|
||||
logger.warn('POW failed for block %s.' % blockHash)
|
||||
else:
|
||||
|
@ -110,6 +111,7 @@ def download_blocks_from_communicator(comm_inst):
|
|||
if removeFromQueue:
|
||||
try:
|
||||
del comm_inst.blockQueue[blockHash] # remove from block queue both if success or false
|
||||
logger.info('%s blocks remaining in queue' % [len(comm_inst.blockQueue)], terminal=True)
|
||||
except KeyError:
|
||||
pass
|
||||
comm_inst.currentDownloading.remove(blockHash)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import sqlite3
|
||||
import logger
|
||||
from onionrusers import onionrusers
|
||||
from onionrutils import epoch
|
||||
def clean_old_blocks(comm_inst):
|
||||
'''Delete old blocks if our disk allocation is full/near full, and also expired blocks'''
|
||||
|
||||
|
@ -29,7 +30,7 @@ def clean_old_blocks(comm_inst):
|
|||
comm_inst._core.removeBlock(bHash)
|
||||
logger.info('Deleted block: %s' % (bHash,))
|
||||
|
||||
while comm_inst._core._utils.storageCounter.isFull():
|
||||
while comm_inst._core.storage_counter.isFull():
|
||||
oldest = comm_inst._core.getBlockList()[0]
|
||||
comm_inst._core._blacklist.addToDB(oldest)
|
||||
comm_inst._core.removeBlock(oldest)
|
||||
|
@ -41,7 +42,7 @@ def clean_keys(comm_inst):
|
|||
'''Delete expired forward secrecy keys'''
|
||||
conn = sqlite3.connect(comm_inst._core.peerDB, timeout=10)
|
||||
c = conn.cursor()
|
||||
time = comm_inst._core._utils.getEpoch()
|
||||
time = epoch.get_epoch()
|
||||
deleteKeys = []
|
||||
|
||||
for entry in c.execute("SELECT * FROM forwardKeys WHERE expire <= ?", (time,)):
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import logger
|
||||
from onionrutils import stringvalidators
|
||||
|
||||
def lookup_new_peer_transports_with_communicator(comm_inst):
|
||||
logger.info('Looking up new addresses...')
|
||||
|
@ -39,7 +40,7 @@ def lookup_new_peer_transports_with_communicator(comm_inst):
|
|||
invalid = []
|
||||
for x in newPeers:
|
||||
x = x.strip()
|
||||
if not comm_inst._core._utils.validateID(x) or x in comm_inst.newPeers or x == comm_inst._core.hsAddress:
|
||||
if not stringvalidators.validate_transport(x) or x in comm_inst.newPeers or x == comm_inst._core.hsAddress:
|
||||
# avoid adding if its our address
|
||||
invalid.append(x)
|
||||
for x in invalid:
|
||||
|
|
|
@ -18,62 +18,71 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import logger, onionrproofs
|
||||
def lookup_blocks_from_communicator(comm_inst):
|
||||
logger.info('Looking up new blocks...')
|
||||
tryAmount = 2
|
||||
newBlocks = ''
|
||||
existingBlocks = comm_inst._core.getBlockList()
|
||||
triedPeers = [] # list of peers we've tried this time around
|
||||
maxBacklog = 1560 # Max amount of *new* block hashes to have already in queue, to avoid memory exhaustion
|
||||
lastLookupTime = 0 # Last time we looked up a particular peer's list
|
||||
for i in range(tryAmount):
|
||||
listLookupCommand = 'getblocklist' # This is defined here to reset it each time
|
||||
if len(comm_inst.blockQueue) >= maxBacklog:
|
||||
break
|
||||
if not comm_inst.isOnline:
|
||||
break
|
||||
# check if disk allocation is used
|
||||
if comm_inst._core._utils.storageCounter.isFull():
|
||||
logger.debug('Not looking up new blocks due to maximum amount of allowed disk space used')
|
||||
break
|
||||
peer = comm_inst.pickOnlinePeer() # select random online peer
|
||||
# if we've already tried all the online peers this time around, stop
|
||||
if peer in triedPeers:
|
||||
if len(comm_inst.onlinePeers) == len(triedPeers):
|
||||
break
|
||||
else:
|
||||
continue
|
||||
triedPeers.append(peer)
|
||||
from onionrutils import stringvalidators, epoch
|
||||
|
||||
# Get the last time we looked up a peer's stamp to only fetch blocks since then.
|
||||
# Saved in memory only for privacy reasons
|
||||
try:
|
||||
lastLookupTime = comm_inst.dbTimestamps[peer]
|
||||
except KeyError:
|
||||
lastLookupTime = 0
|
||||
def lookup_blocks_from_communicator(comm_inst):
|
||||
logger.info('Looking up new blocks...')
|
||||
tryAmount = 2
|
||||
newBlocks = ''
|
||||
existingBlocks = comm_inst._core.getBlockList()
|
||||
triedPeers = [] # list of peers we've tried this time around
|
||||
maxBacklog = 1560 # Max amount of *new* block hashes to have already in queue, to avoid memory exhaustion
|
||||
lastLookupTime = 0 # Last time we looked up a particular peer's list
|
||||
new_block_count = 0
|
||||
for i in range(tryAmount):
|
||||
listLookupCommand = 'getblocklist' # This is defined here to reset it each time
|
||||
if len(comm_inst.blockQueue) >= maxBacklog:
|
||||
break
|
||||
if not comm_inst.isOnline:
|
||||
break
|
||||
# check if disk allocation is used
|
||||
if comm_inst._core.storage_counter.isFull():
|
||||
logger.debug('Not looking up new blocks due to maximum amount of allowed disk space used')
|
||||
break
|
||||
peer = comm_inst.pickOnlinePeer() # select random online peer
|
||||
# if we've already tried all the online peers this time around, stop
|
||||
if peer in triedPeers:
|
||||
if len(comm_inst.onlinePeers) == len(triedPeers):
|
||||
break
|
||||
else:
|
||||
listLookupCommand += '?date=%s' % (lastLookupTime,)
|
||||
try:
|
||||
newBlocks = comm_inst.peerAction(peer, listLookupCommand) # get list of new block hashes
|
||||
except Exception as error:
|
||||
logger.warn('Could not get new blocks from %s.' % peer, error = error)
|
||||
newBlocks = False
|
||||
else:
|
||||
comm_inst.dbTimestamps[peer] = comm_inst._core._utils.getRoundedEpoch(roundS=60)
|
||||
if newBlocks != False:
|
||||
# if request was a success
|
||||
for i in newBlocks.split('\n'):
|
||||
if comm_inst._core._utils.validateHash(i):
|
||||
# if newline seperated string is valid hash
|
||||
if not i in existingBlocks:
|
||||
# if block does not exist on disk and is not already in block queue
|
||||
if i not in comm_inst.blockQueue:
|
||||
if onionrproofs.hashMeetsDifficulty(i) and not comm_inst._core._blacklist.inBlacklist(i):
|
||||
if len(comm_inst.blockQueue) <= 1000000:
|
||||
comm_inst.blockQueue[i] = [peer] # add blocks to download queue
|
||||
else:
|
||||
if peer not in comm_inst.blockQueue[i]:
|
||||
if len(comm_inst.blockQueue[i]) < 10:
|
||||
comm_inst.blockQueue[i].append(peer)
|
||||
comm_inst.decrementThreadCount('lookupBlocks')
|
||||
return
|
||||
continue
|
||||
triedPeers.append(peer)
|
||||
|
||||
# Get the last time we looked up a peer's stamp to only fetch blocks since then.
|
||||
# Saved in memory only for privacy reasons
|
||||
try:
|
||||
lastLookupTime = comm_inst.dbTimestamps[peer]
|
||||
except KeyError:
|
||||
lastLookupTime = 0
|
||||
else:
|
||||
listLookupCommand += '?date=%s' % (lastLookupTime,)
|
||||
try:
|
||||
newBlocks = comm_inst.peerAction(peer, listLookupCommand) # get list of new block hashes
|
||||
except Exception as error:
|
||||
logger.warn('Could not get new blocks from %s.' % peer, error = error)
|
||||
newBlocks = False
|
||||
else:
|
||||
comm_inst.dbTimestamps[peer] = epoch.get_rounded_epoch(roundS=60)
|
||||
if newBlocks != False:
|
||||
# if request was a success
|
||||
for i in newBlocks.split('\n'):
|
||||
if stringvalidators.validate_hash(i):
|
||||
# if newline seperated string is valid hash
|
||||
if not i in existingBlocks:
|
||||
# if block does not exist on disk and is not already in block queue
|
||||
if i not in comm_inst.blockQueue:
|
||||
if onionrproofs.hashMeetsDifficulty(i) and not comm_inst._core._blacklist.inBlacklist(i):
|
||||
if len(comm_inst.blockQueue) <= 1000000:
|
||||
comm_inst.blockQueue[i] = [peer] # add blocks to download queue
|
||||
new_block_count += 1
|
||||
else:
|
||||
if peer not in comm_inst.blockQueue[i]:
|
||||
if len(comm_inst.blockQueue[i]) < 10:
|
||||
comm_inst.blockQueue[i].append(peer)
|
||||
if new_block_count > 0:
|
||||
block_string = ""
|
||||
if new_block_count > 1:
|
||||
block_string = "s"
|
||||
logger.info('Discovered %s new block%s' % (new_block_count, block_string), terminal=True)
|
||||
comm_inst.decrementThreadCount('lookupBlocks')
|
||||
return
|
|
@ -20,17 +20,19 @@
|
|||
'''
|
||||
import logger
|
||||
from utils import netutils
|
||||
from onionrutils import localcommand, epoch
|
||||
def net_check(comm_inst):
|
||||
'''Check if we are connected to the internet or not when we can't connect to any peers'''
|
||||
rec = False # for detecting if we have received incoming connections recently
|
||||
c = comm_inst._core
|
||||
if len(comm_inst.onlinePeers) == 0:
|
||||
try:
|
||||
if (comm_inst._core._utils.getEpoch() - int(comm_inst._core._utils.localCommand('/lastconnect'))) <= 60:
|
||||
if (epoch.get_epoch() - int(localcommand.local_command(c, '/lastconnect'))) <= 60:
|
||||
comm_inst.isOnline = True
|
||||
rec = True
|
||||
except ValueError:
|
||||
pass
|
||||
if not rec and not netutils.checkNetwork(comm_inst._core._utils, torPort=comm_inst.proxyPort):
|
||||
if not rec and not netutils.checkNetwork(c, torPort=comm_inst.proxyPort):
|
||||
if not comm_inst.shutdown:
|
||||
logger.warn('Network check failed, are you connected to the Internet, and is Tor working?')
|
||||
comm_inst.isOnline = False
|
||||
|
|
|
@ -55,7 +55,7 @@ class OnionrCommunicatorTimers:
|
|||
logger.debug('%s is currently using the maximum number of threads, not starting another.' % self.timerFunction.__name__)
|
||||
else:
|
||||
self.daemonInstance.threadCounts[self.timerFunction.__name__] += 1
|
||||
newThread = threading.Thread(target=self.timerFunction, args=self.args)
|
||||
newThread = threading.Thread(target=self.timerFunction, args=self.args, daemon=True)
|
||||
newThread.start()
|
||||
else:
|
||||
self.timerFunction()
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import communicator, onionrblockapi
|
||||
from onionrutils import stringvalidators
|
||||
|
||||
def service_creator(daemon):
|
||||
assert isinstance(daemon, communicator.OnionrCommunicatorDaemon)
|
||||
core = daemon._core
|
||||
|
@ -30,7 +32,7 @@ def service_creator(daemon):
|
|||
if not b in daemon.active_services:
|
||||
bl = onionrblockapi.Block(b, core=core, decrypt=True)
|
||||
bs = utils.bytesToStr(bl.bcontent) + '.onion'
|
||||
if utils.validatePubKey(bl.signer) and utils.validateID(bs):
|
||||
if stringvalidators.validate_pub_key(bl.signer) and stringvalidators.validate_transport(bs):
|
||||
signer = utils.bytesToStr(bl.signer)
|
||||
daemon.active_services.append(b)
|
||||
daemon.active_services.append(signer)
|
||||
|
|
|
@ -20,15 +20,17 @@
|
|||
import logger
|
||||
from communicatorutils import proxypicker
|
||||
import onionrblockapi as block
|
||||
from onionrutils import localcommand, stringvalidators, basicrequests
|
||||
|
||||
def upload_blocks_from_communicator(comm_inst):
|
||||
# when inserting a block, we try to upload it to a few peers to add some deniability
|
||||
triedPeers = []
|
||||
finishedUploads = []
|
||||
comm_inst.blocksToUpload = comm_inst._core._crypto.randomShuffle(comm_inst.blocksToUpload)
|
||||
core = comm_inst._core
|
||||
comm_inst.blocksToUpload = core._crypto.randomShuffle(comm_inst.blocksToUpload)
|
||||
if len(comm_inst.blocksToUpload) != 0:
|
||||
for bl in comm_inst.blocksToUpload:
|
||||
if not comm_inst._core._utils.validateHash(bl):
|
||||
if not stringvalidators.validate_hash(bl):
|
||||
logger.warn('Requested to upload invalid block')
|
||||
comm_inst.decrementThreadCount('uploadBlock')
|
||||
return
|
||||
|
@ -40,9 +42,9 @@ def upload_blocks_from_communicator(comm_inst):
|
|||
url = 'http://' + peer + '/upload'
|
||||
data = {'block': block.Block(bl).getRaw()}
|
||||
proxyType = proxypicker.pick_proxy(peer)
|
||||
logger.info("Uploading block to " + peer)
|
||||
if not comm_inst._core._utils.doPostRequest(url, data=data, proxyType=proxyType) == False:
|
||||
comm_inst._core._utils.localCommand('waitforshare/' + bl, post=True)
|
||||
logger.info("Uploading block to " + peer, terminal=True)
|
||||
if not basicrequests.do_post_request(core, url, data=data, proxyType=proxyType) == False:
|
||||
localcommand.local_command(core, 'waitforshare/' + bl, post=True)
|
||||
finishedUploads.append(bl)
|
||||
for x in finishedUploads:
|
||||
try:
|
||||
|
|
486
onionr/core.py
486
onionr/core.py
|
@ -17,22 +17,20 @@
|
|||
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 sqlite3, os, sys, time, json, uuid
|
||||
import os, sys, json
|
||||
import logger, netcontroller, config
|
||||
from onionrblockapi import Block
|
||||
import coredb
|
||||
import deadsimplekv as simplekv
|
||||
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions
|
||||
import onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions
|
||||
import onionrblacklist
|
||||
from onionrusers import onionrusers
|
||||
from onionrstorage import removeblock, setdata
|
||||
import dbcreator, onionrstorage, serializeddata, subprocesspow
|
||||
from etc import onionrvalues, powchoice
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
try:
|
||||
import sha3
|
||||
except ModuleNotFoundError:
|
||||
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
|
||||
sys.exit(1)
|
||||
from onionrutils import localcommand, stringvalidators, bytesconverter, epoch
|
||||
from onionrutils import blockmetadata
|
||||
import storagecounter
|
||||
|
||||
class Core:
|
||||
def __init__(self, torPort=0):
|
||||
|
@ -45,6 +43,10 @@ class Core:
|
|||
self.dataDir += '/'
|
||||
|
||||
try:
|
||||
self.usageFile = self.dataDir + 'disk-usage.txt'
|
||||
self.config = config
|
||||
self.maxBlockSize = 10000000 # max block size in bytes
|
||||
|
||||
self.onionrInst = None
|
||||
self.queueDB = self.dataDir + 'queue.db'
|
||||
self.peerDB = self.dataDir + 'peers.db'
|
||||
|
@ -64,6 +66,7 @@ class Core:
|
|||
self.dbCreate = dbcreator.DBCreator(self)
|
||||
self.forwardKeysFile = self.dataDir + 'forward-keys.db'
|
||||
self.keyStore = simplekv.DeadSimpleKV(self.dataDir + 'cachedstorage.dat', refresh_seconds=5)
|
||||
self.storage_counter = storagecounter.StorageCounter(self)
|
||||
|
||||
# Socket data, defined here because of multithreading constraints with gevent
|
||||
self.killSockets = False
|
||||
|
@ -72,11 +75,6 @@ class Core:
|
|||
self.socketReasons = {}
|
||||
self.socketServerResponseData = {}
|
||||
|
||||
self.usageFile = self.dataDir + 'disk-usage.txt'
|
||||
self.config = config
|
||||
|
||||
self.maxBlockSize = 10000000 # max block size in bytes
|
||||
|
||||
if not os.path.exists(self.dataDir):
|
||||
os.mkdir(self.dataDir)
|
||||
if not os.path.exists(self.dataDir + 'blocks/'):
|
||||
|
@ -104,15 +102,14 @@ class Core:
|
|||
logger.warn('Warning: address bootstrap file not found ' + self.bootstrapFileLocation)
|
||||
|
||||
self.use_subprocess = powchoice.use_subprocess(self)
|
||||
self._utils = onionrutils.OnionrUtils(self)
|
||||
# Initialize the crypto object
|
||||
self._crypto = onionrcrypto.OnionrCrypto(self)
|
||||
self._blacklist = onionrblacklist.OnionrBlackList(self)
|
||||
self.serializer = serializeddata.SerializedData(self)
|
||||
|
||||
except Exception as error:
|
||||
logger.error('Failed to initialize core Onionr library.', error=error)
|
||||
logger.fatal('Cannot recover from error.')
|
||||
logger.error('Failed to initialize core Onionr library.', error=error, terminal=True)
|
||||
logger.fatal('Cannot recover from error.', terminal=True)
|
||||
sys.exit(1)
|
||||
return
|
||||
|
||||
|
@ -128,88 +125,19 @@ class Core:
|
|||
'''
|
||||
Adds a public key to the key database (misleading function name)
|
||||
'''
|
||||
assert peerID not in self.listPeers()
|
||||
|
||||
# This function simply adds a peer to the DB
|
||||
if not self._utils.validatePubKey(peerID):
|
||||
return False
|
||||
|
||||
events.event('pubkey_add', data = {'key': peerID}, onionr = self.onionrInst)
|
||||
|
||||
conn = sqlite3.connect(self.peerDB, timeout=30)
|
||||
hashID = self._crypto.pubKeyHashID(peerID)
|
||||
c = conn.cursor()
|
||||
t = (peerID, name, 'unknown', hashID, 0)
|
||||
|
||||
for i in c.execute("SELECT * FROM peers WHERE id = ?;", (peerID,)):
|
||||
try:
|
||||
if i[0] == peerID:
|
||||
conn.close()
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
c.execute('INSERT INTO peers (id, name, dateSeen, hashID, trust) VALUES(?, ?, ?, ?, ?);', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
return coredb.keydb.addkeys.add_peer(self, peerID, name)
|
||||
|
||||
def addAddress(self, address):
|
||||
'''
|
||||
Add an address to the address database (only tor currently)
|
||||
'''
|
||||
|
||||
if type(address) is None or len(address) == 0:
|
||||
return False
|
||||
if self._utils.validateID(address):
|
||||
if address == config.get('i2p.ownAddr', None) or address == self.hsAddress:
|
||||
return False
|
||||
conn = sqlite3.connect(self.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
# check if address is in database
|
||||
# this is safe to do because the address is validated above, but we strip some chars here too just in case
|
||||
address = address.replace('\'', '').replace(';', '').replace('"', '').replace('\\', '')
|
||||
for i in c.execute("SELECT * FROM adders WHERE address = ?;", (address,)):
|
||||
try:
|
||||
if i[0] == address:
|
||||
conn.close()
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
t = (address, 1)
|
||||
c.execute('INSERT INTO adders (address, type) VALUES(?, ?);', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('address_add', data = {'address': address}, onionr = self.onionrInst)
|
||||
|
||||
return True
|
||||
else:
|
||||
#logger.debug('Invalid ID: %s' % address)
|
||||
return False
|
||||
return coredb.keydb.addkeys.add_address(self, address)
|
||||
|
||||
def removeAddress(self, address):
|
||||
'''
|
||||
Remove an address from the address database
|
||||
'''
|
||||
|
||||
if self._utils.validateID(address):
|
||||
conn = sqlite3.connect(self.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (address,)
|
||||
c.execute('Delete from adders where address=?;', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('address_remove', data = {'address': address}, onionr = self.onionrInst)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return coredb.keydb.removekeys.remove_address(self, address)
|
||||
|
||||
def removeBlock(self, block):
|
||||
'''
|
||||
|
@ -217,18 +145,7 @@ class Core:
|
|||
|
||||
**You may want blacklist.addToDB(blockHash)
|
||||
'''
|
||||
|
||||
if self._utils.validateHash(block):
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (block,)
|
||||
c.execute('Delete from hashes where hash=?;', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
dataSize = sys.getsizeof(onionrstorage.getData(self, block))
|
||||
self._utils.storageCounter.removeBytes(dataSize)
|
||||
else:
|
||||
raise onionrexceptions.InvalidHexHash
|
||||
removeblock.remove_block(self, block)
|
||||
|
||||
def createAddressDB(self):
|
||||
'''
|
||||
|
@ -254,67 +171,19 @@ class Core:
|
|||
|
||||
Should be in hex format!
|
||||
'''
|
||||
|
||||
if not os.path.exists(self.blockDB):
|
||||
raise Exception('Block db does not exist')
|
||||
if self._utils.hasBlock(newHash):
|
||||
return
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
currentTime = self._utils.getEpoch() + self._crypto.secrets.randbelow(301)
|
||||
if selfInsert or dataSaved:
|
||||
selfInsert = 1
|
||||
else:
|
||||
selfInsert = 0
|
||||
data = (newHash, currentTime, '', selfInsert)
|
||||
c.execute('INSERT INTO hashes (hash, dateReceived, dataType, dataSaved) VALUES(?, ?, ?, ?);', data)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return
|
||||
|
||||
def getData(self, hash):
|
||||
'''
|
||||
Simply return the data associated to a hash
|
||||
'''
|
||||
|
||||
data = onionrstorage.getData(self, hash)
|
||||
|
||||
return data
|
||||
coredb.blockmetadb.add.add_to_block_DB(self, newHash, selfInsert, dataSaved)
|
||||
|
||||
def setData(self, data):
|
||||
'''
|
||||
Set the data assciated with a hash
|
||||
'''
|
||||
return onionrstorage.setdata.set_data(self, data)
|
||||
|
||||
data = data
|
||||
dataSize = sys.getsizeof(data)
|
||||
|
||||
if not type(data) is bytes:
|
||||
data = data.encode()
|
||||
|
||||
dataHash = self._crypto.sha3Hash(data)
|
||||
|
||||
if type(dataHash) is bytes:
|
||||
dataHash = dataHash.decode()
|
||||
blockFileName = self.blockDataLocation + dataHash + '.dat'
|
||||
if os.path.exists(blockFileName):
|
||||
pass # TODO: properly check if block is already saved elsewhere
|
||||
#raise Exception("Data is already set for " + dataHash)
|
||||
else:
|
||||
if self._utils.storageCounter.addBytes(dataSize) != False:
|
||||
onionrstorage.store(self, data, blockHash=dataHash)
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE hashes SET dataSaved=1 WHERE hash = ?;", (dataHash,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
with open(self.dataNonceFile, 'a') as nonceFile:
|
||||
nonceFile.write(dataHash + '\n')
|
||||
else:
|
||||
raise onionrexceptions.DiskAllocationReached
|
||||
|
||||
return dataHash
|
||||
def getData(self, hash):
|
||||
'''
|
||||
Simply return the data associated to a hash
|
||||
'''
|
||||
return onionrstorage.getData(self, hash)
|
||||
|
||||
def daemonQueue(self):
|
||||
'''
|
||||
|
@ -322,117 +191,31 @@ class Core:
|
|||
|
||||
This function intended to be used by the client. Queue to exchange data between "client" and server.
|
||||
'''
|
||||
|
||||
retData = False
|
||||
if not os.path.exists(self.queueDB):
|
||||
self.dbCreate.createDaemonDB()
|
||||
else:
|
||||
conn = sqlite3.connect(self.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
try:
|
||||
for row in c.execute('SELECT command, data, date, min(ID), responseID FROM commands group by id'):
|
||||
retData = row
|
||||
break
|
||||
except sqlite3.OperationalError:
|
||||
self.dbCreate.createDaemonDB()
|
||||
else:
|
||||
if retData != False:
|
||||
c.execute('DELETE FROM commands WHERE id=?;', (retData[3],))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('queue_pop', data = {'data': retData}, onionr = self.onionrInst)
|
||||
|
||||
return retData
|
||||
return coredb.daemonqueue.daemon_queue(self)
|
||||
|
||||
def daemonQueueAdd(self, command, data='', responseID=''):
|
||||
'''
|
||||
Add a command to the daemon queue, used by the communication daemon (communicator.py)
|
||||
'''
|
||||
|
||||
retData = True
|
||||
|
||||
date = self._utils.getEpoch()
|
||||
conn = sqlite3.connect(self.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (command, data, date, responseID)
|
||||
try:
|
||||
c.execute('INSERT INTO commands (command, data, date, responseID) VALUES(?, ?, ?, ?)', t)
|
||||
conn.commit()
|
||||
except sqlite3.OperationalError:
|
||||
retData = False
|
||||
self.daemonQueue()
|
||||
events.event('queue_push', data = {'command': command, 'data': data}, onionr = self.onionrInst)
|
||||
conn.close()
|
||||
return retData
|
||||
return coredb.daemonqueue.daemon_queue_add(self, command, data, responseID)
|
||||
|
||||
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/' + responseID)
|
||||
return resp
|
||||
|
||||
def daemonQueueWaitForResponse(self, responseID='', checkFreqSecs=1):
|
||||
resp = 'failure'
|
||||
while resp == 'failure':
|
||||
resp = self.daemonQueueGetResponse(responseID)
|
||||
time.sleep(1)
|
||||
return resp
|
||||
|
||||
def daemonQueueSimple(self, command, data='', checkFreqSecs=1):
|
||||
'''
|
||||
A simplified way to use the daemon queue. Will register a command (with optional data) and wait, return the data
|
||||
Not always useful, but saves time + LOC in some cases.
|
||||
This is a blocking function, so be careful.
|
||||
'''
|
||||
responseID = str(uuid.uuid4()) # generate unique response ID
|
||||
self.daemonQueueAdd(command, data=data, responseID=responseID)
|
||||
return self.daemonQueueWaitForResponse(responseID, checkFreqSecs)
|
||||
return coredb.daemonqueue.daemon_queue_get_response(self, responseID)
|
||||
|
||||
def clearDaemonQueue(self):
|
||||
'''
|
||||
Clear the daemon queue (somewhat dangerous)
|
||||
'''
|
||||
conn = sqlite3.connect(self.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
try:
|
||||
c.execute('DELETE FROM commands;')
|
||||
conn.commit()
|
||||
except:
|
||||
pass
|
||||
|
||||
conn.close()
|
||||
events.event('queue_clear', onionr = self.onionrInst)
|
||||
|
||||
return
|
||||
return coredb.daemonqueue.clear_daemon_queue(self)
|
||||
|
||||
def listAdders(self, randomOrder=True, i2p=True, recent=0):
|
||||
'''
|
||||
Return a list of addresses
|
||||
'''
|
||||
conn = sqlite3.connect(self.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
if randomOrder:
|
||||
addresses = c.execute('SELECT * FROM adders ORDER BY RANDOM();')
|
||||
else:
|
||||
addresses = c.execute('SELECT * FROM adders;')
|
||||
addressList = []
|
||||
for i in addresses:
|
||||
if len(i[0].strip()) == 0:
|
||||
continue
|
||||
addressList.append(i[0])
|
||||
conn.close()
|
||||
testList = list(addressList) # create new list to iterate
|
||||
for address in testList:
|
||||
try:
|
||||
if recent > 0 and (self._utils.getEpoch() - self.getAddressInfo(address, 'lastConnect')) > recent:
|
||||
raise TypeError # If there is no last-connected date or it was too long ago, don't add peer to list if recent is not 0
|
||||
except TypeError:
|
||||
addressList.remove(address)
|
||||
return addressList
|
||||
return coredb.keydb.listkeys.list_adders(self, randomOrder, i2p, recent)
|
||||
|
||||
def listPeers(self, randomOrder=True, getPow=False, trust=0):
|
||||
'''
|
||||
|
@ -441,35 +224,7 @@ class Core:
|
|||
randomOrder determines if the list should be in a random order
|
||||
trust sets the minimum trust to list
|
||||
'''
|
||||
conn = sqlite3.connect(self.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
payload = ''
|
||||
|
||||
if trust not in (0, 1, 2):
|
||||
logger.error('Tried to select invalid trust.')
|
||||
return
|
||||
|
||||
if randomOrder:
|
||||
payload = 'SELECT * FROM peers WHERE trust >= ? ORDER BY RANDOM();'
|
||||
else:
|
||||
payload = 'SELECT * FROM peers WHERE trust >= ?;'
|
||||
|
||||
peerList = []
|
||||
|
||||
for i in c.execute(payload, (trust,)):
|
||||
try:
|
||||
if len(i[0]) != 0:
|
||||
if getPow:
|
||||
peerList.append(i[0] + '-' + i[1])
|
||||
else:
|
||||
peerList.append(i[0])
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
conn.close()
|
||||
|
||||
return peerList
|
||||
return coredb.keydb.listkeys.list_peers(self, randomOrder, getPow, trust)
|
||||
|
||||
def getPeerInfo(self, peer, info):
|
||||
'''
|
||||
|
@ -482,46 +237,13 @@ class Core:
|
|||
trust int 4
|
||||
hashID text 5
|
||||
'''
|
||||
conn = sqlite3.connect(self.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (peer,)
|
||||
infoNumbers = {'id': 0, 'name': 1, 'adders': 2, 'dateSeen': 3, 'trust': 4, 'hashID': 5}
|
||||
info = infoNumbers[info]
|
||||
iterCount = 0
|
||||
retVal = ''
|
||||
|
||||
for row in c.execute('SELECT * FROM peers WHERE id=?;', command):
|
||||
for i in row:
|
||||
if iterCount == info:
|
||||
retVal = i
|
||||
break
|
||||
else:
|
||||
iterCount += 1
|
||||
|
||||
conn.close()
|
||||
|
||||
return retVal
|
||||
return coredb.keydb.userinfo.get_user_info(self, peer, info)
|
||||
|
||||
def setPeerInfo(self, peer, key, data):
|
||||
'''
|
||||
Update a peer for a key
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(self.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (data, peer)
|
||||
|
||||
# TODO: validate key on whitelist
|
||||
if key not in ('id', 'name', 'pubkey', 'forwardKey', 'dateSeen', 'trust'):
|
||||
raise Exception("Got invalid database key when setting peer info")
|
||||
|
||||
c.execute('UPDATE peers SET ' + key + ' = ? WHERE id=?', command)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return
|
||||
return coredb.keydb.userinfo.set_peer_info(self, peer, key, data)
|
||||
|
||||
def getAddressInfo(self, address, info):
|
||||
'''
|
||||
|
@ -538,117 +260,35 @@ class Core:
|
|||
trust 8
|
||||
introduced 9
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(self.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (address,)
|
||||
infoNumbers = {'address': 0, 'type': 1, 'knownPeer': 2, 'speed': 3, 'success': 4, 'powValue': 5, 'failure': 6, 'lastConnect': 7, 'trust': 8, 'introduced': 9}
|
||||
info = infoNumbers[info]
|
||||
iterCount = 0
|
||||
retVal = ''
|
||||
|
||||
for row in c.execute('SELECT * FROM adders WHERE address=?;', command):
|
||||
for i in row:
|
||||
if iterCount == info:
|
||||
retVal = i
|
||||
break
|
||||
else:
|
||||
iterCount += 1
|
||||
conn.close()
|
||||
|
||||
return retVal
|
||||
return coredb.keydb.transportinfo.get_address_info(self, address, info)
|
||||
|
||||
def setAddressInfo(self, address, key, data):
|
||||
'''
|
||||
Update an address for a key
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(self.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (data, address)
|
||||
|
||||
if key not in ('address', 'type', 'knownPeer', 'speed', 'success', 'failure', 'powValue', 'lastConnect', 'lastConnectAttempt', 'trust', 'introduced'):
|
||||
raise Exception("Got invalid database key when setting address info")
|
||||
else:
|
||||
c.execute('UPDATE adders SET ' + key + ' = ? WHERE address=?', command)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return
|
||||
return coredb.keydb.transportinfo.set_address_info(self, address, key, data)
|
||||
|
||||
def getBlockList(self, dateRec = None, unsaved = False):
|
||||
'''
|
||||
Get list of our blocks
|
||||
'''
|
||||
if dateRec == None:
|
||||
dateRec = 0
|
||||
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
execute = 'SELECT hash FROM hashes WHERE dateReceived >= ? ORDER BY dateReceived ASC;'
|
||||
args = (dateRec,)
|
||||
rows = list()
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
||||
return coredb.blockmetadb.get_block_list(self, dateRec, unsaved)
|
||||
|
||||
def getBlockDate(self, blockHash):
|
||||
'''
|
||||
Returns the date a block was received
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
execute = 'SELECT dateReceived FROM hashes WHERE hash=?;'
|
||||
args = (blockHash,)
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
return int(i)
|
||||
conn.close()
|
||||
return None
|
||||
return coredb.blockmetadb.get_block_date(self, blockHash)
|
||||
|
||||
def getBlocksByType(self, blockType, orderDate=True):
|
||||
'''
|
||||
Returns a list of blocks by the type
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
if orderDate:
|
||||
execute = 'SELECT hash FROM hashes WHERE dataType=? ORDER BY dateReceived;'
|
||||
else:
|
||||
execute = 'SELECT hash FROM hashes WHERE dataType=?;'
|
||||
|
||||
args = (blockType,)
|
||||
rows = list()
|
||||
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
||||
return coredb.blockmetadb.get_blocks_by_type(self, blockType, orderDate)
|
||||
|
||||
def getExpiredBlocks(self):
|
||||
'''Returns a list of expired blocks'''
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
date = int(self._utils.getEpoch())
|
||||
|
||||
execute = 'SELECT hash FROM hashes WHERE expire <= %s ORDER BY dateReceived;' % (date,)
|
||||
|
||||
rows = list()
|
||||
for row in c.execute(execute):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
||||
return coredb.blockmetadb.expiredblocks.get_expired_blocks(self)
|
||||
|
||||
def updateBlockInfo(self, hash, key, data):
|
||||
'''
|
||||
|
@ -665,18 +305,7 @@ class Core:
|
|||
dateClaimed - timestamp claimed inside the block, only as trustworthy as the block author is
|
||||
expire - expire date for a block
|
||||
'''
|
||||
|
||||
if key not in ('dateReceived', 'decrypted', 'dataType', 'dataFound', 'dataSaved', 'sig', 'author', 'dateClaimed', 'expire'):
|
||||
return False
|
||||
|
||||
conn = sqlite3.connect(self.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
args = (data, hash)
|
||||
c.execute("UPDATE hashes SET " + key + " = ? where hash = ?;", args)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
return coredb.blockmetadb.updateblockinfo.update_block_info(self, hash, key, data)
|
||||
|
||||
def insertBlock(self, data, header='txt', sign=False, encryptType='', symKey='', asymPeer='', meta = {}, expire=None, disableForward=False):
|
||||
'''
|
||||
|
@ -684,7 +313,7 @@ class Core:
|
|||
encryptType must be specified to encrypt a block
|
||||
'''
|
||||
allocationReachedMessage = 'Cannot insert block, disk allocation reached.'
|
||||
if self._utils.storageCounter.isFull():
|
||||
if self.storage_counter.isFull():
|
||||
logger.error(allocationReachedMessage)
|
||||
return False
|
||||
retData = False
|
||||
|
@ -692,11 +321,9 @@ class Core:
|
|||
if type(data) is None:
|
||||
raise ValueError('Data cannot be none')
|
||||
|
||||
createTime = self._utils.getRoundedEpoch()
|
||||
createTime = epoch.get_epoch()
|
||||
|
||||
# check nonce
|
||||
#print(data)
|
||||
dataNonce = self._utils.bytesToStr(self._crypto.sha3Hash(data))
|
||||
dataNonce = bytesconverter.bytes_to_str(self._crypto.sha3Hash(data))
|
||||
try:
|
||||
with open(self.dataNonceFile, 'r') as nonces:
|
||||
if dataNonce in nonces:
|
||||
|
@ -769,14 +396,18 @@ class Core:
|
|||
signature = self._crypto.symmetricEncrypt(signature, key=symKey, returnEncoded=True).decode()
|
||||
signer = self._crypto.symmetricEncrypt(signer, key=symKey, returnEncoded=True).decode()
|
||||
elif encryptType == 'asym':
|
||||
if self._utils.validatePubKey(asymPeer):
|
||||
if stringvalidators.validate_pub_key(asymPeer):
|
||||
# Encrypt block data with forward secrecy key first, but not meta
|
||||
jsonMeta = json.dumps(meta)
|
||||
jsonMeta = self._crypto.pubKeyEncrypt(jsonMeta, asymPeer, encodedData=True).decode()
|
||||
data = self._crypto.pubKeyEncrypt(data, asymPeer, encodedData=True).decode()
|
||||
signature = self._crypto.pubKeyEncrypt(signature, asymPeer, encodedData=True).decode()
|
||||
signer = self._crypto.pubKeyEncrypt(signer, asymPeer, encodedData=True).decode()
|
||||
onionrusers.OnionrUser(self, asymPeer, saveUser=True)
|
||||
try:
|
||||
onionrusers.OnionrUser(self, asymPeer, saveUser=True)
|
||||
except ValueError:
|
||||
# if peer is already known
|
||||
pass
|
||||
else:
|
||||
raise onionrexceptions.InvalidPubkey(asymPeer + ' is not a valid base32 encoded ed25519 key')
|
||||
|
||||
|
@ -804,25 +435,28 @@ class Core:
|
|||
retData = False
|
||||
else:
|
||||
# Tell the api server through localCommand to wait for the daemon to upload this block to make statistical analysis more difficult
|
||||
if self._utils.localCommand('/ping', maxWait=10) == 'pong!':
|
||||
self._utils.localCommand('/waitforshare/' + retData, post=True, maxWait=5)
|
||||
if localcommand.local_command(self, '/ping', maxWait=10) == 'pong!':
|
||||
if self.config.get('general.security_level', 1) == 0:
|
||||
localcommand.local_command(self, '/waitforshare/' + retData, post=True, maxWait=5)
|
||||
self.daemonQueueAdd('uploadBlock', retData)
|
||||
else:
|
||||
pass
|
||||
self.addToBlockDB(retData, selfInsert=True, dataSaved=True)
|
||||
self._utils.processBlockMetadata(retData)
|
||||
blockmetadata.process_block_metadata(self, retData)
|
||||
|
||||
if retData != False:
|
||||
if plaintextPeer == onionrvalues.DENIABLE_PEER_ADDRESS:
|
||||
events.event('insertdeniable', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': self._utils.bytesToStr(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
events.event('insertdeniable', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
else:
|
||||
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': self._utils.bytesToStr(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
return retData
|
||||
|
||||
def introduceNode(self):
|
||||
'''
|
||||
Introduces our node into the network by telling X many nodes our HS address
|
||||
'''
|
||||
if self._utils.localCommand('/ping', maxWait=10) == 'pong!':
|
||||
if localcommand.local_command(self, '/ping', maxWait=10) == 'pong!':
|
||||
self.daemonQueueAdd('announceNode')
|
||||
logger.info('Introduction command will be processed.')
|
||||
logger.info('Introduction command will be processed.', terminal=True)
|
||||
else:
|
||||
logger.warn('No running node detected. Cannot introduce.')
|
||||
logger.warn('No running node detected. Cannot introduce.', terminal=True)
|
1
onionr/coredb/__init__.py
Normal file
1
onionr/coredb/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from . import keydb, blockmetadb, daemonqueue
|
77
onionr/coredb/blockmetadb/__init__.py
Normal file
77
onionr/coredb/blockmetadb/__init__.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This module works with information relating to blocks stored on the node
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
from . import expiredblocks, updateblockinfo, add
|
||||
def get_block_list(core_inst, dateRec = None, unsaved = False):
|
||||
'''
|
||||
Get list of our blocks
|
||||
'''
|
||||
if dateRec == None:
|
||||
dateRec = 0
|
||||
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
execute = 'SELECT hash FROM hashes WHERE dateReceived >= ? ORDER BY dateReceived ASC;'
|
||||
args = (dateRec,)
|
||||
rows = list()
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
def get_block_date(core_inst, blockHash):
|
||||
'''
|
||||
Returns the date a block was received
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
execute = 'SELECT dateReceived FROM hashes WHERE hash=?;'
|
||||
args = (blockHash,)
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
return int(i)
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
def get_blocks_by_type(core_inst, blockType, orderDate=True):
|
||||
'''
|
||||
Returns a list of blocks by the type
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
if orderDate:
|
||||
execute = 'SELECT hash FROM hashes WHERE dataType=? ORDER BY dateReceived;'
|
||||
else:
|
||||
execute = 'SELECT hash FROM hashes WHERE dataType=?;'
|
||||
|
||||
args = (blockType,)
|
||||
rows = list()
|
||||
|
||||
for row in c.execute(execute, args):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
43
onionr/coredb/blockmetadb/add.py
Normal file
43
onionr/coredb/blockmetadb/add.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Add an entry to the block metadata database
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 os, sqlite3
|
||||
from onionrutils import epoch, blockmetadata
|
||||
def add_to_block_DB(core_inst, newHash, selfInsert=False, dataSaved=False):
|
||||
'''
|
||||
Add a hash value to the block db
|
||||
|
||||
Should be in hex format!
|
||||
'''
|
||||
|
||||
if not os.path.exists(core_inst.blockDB):
|
||||
raise Exception('Block db does not exist')
|
||||
if blockmetadata.has_block(core_inst, newHash):
|
||||
return
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
currentTime = epoch.get_epoch() + core_inst._crypto.secrets.randbelow(301)
|
||||
if selfInsert or dataSaved:
|
||||
selfInsert = 1
|
||||
else:
|
||||
selfInsert = 0
|
||||
data = (newHash, currentTime, '', selfInsert)
|
||||
c.execute('INSERT INTO hashes (hash, dateReceived, dataType, dataSaved) VALUES(?, ?, ?, ?);', data)
|
||||
conn.commit()
|
||||
conn.close()
|
35
onionr/coredb/blockmetadb/expiredblocks.py
Normal file
35
onionr/coredb/blockmetadb/expiredblocks.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Get a list of expired blocks still stored
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
from onionrutils import epoch
|
||||
def get_expired_blocks(core_inst):
|
||||
'''Returns a list of expired blocks'''
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
date = int(epoch.get_epoch())
|
||||
|
||||
execute = 'SELECT hash FROM hashes WHERE expire <= %s ORDER BY dateReceived;' % (date,)
|
||||
|
||||
rows = list()
|
||||
for row in c.execute(execute):
|
||||
for i in row:
|
||||
rows.append(i)
|
||||
conn.close()
|
||||
return rows
|
33
onionr/coredb/blockmetadb/updateblockinfo.py
Normal file
33
onionr/coredb/blockmetadb/updateblockinfo.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Update block information in the metadata database by a field name
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
def update_block_info(core_inst, hash, key, data):
|
||||
if key not in ('dateReceived', 'decrypted', 'dataType', 'dataFound', 'dataSaved', 'sig', 'author', 'dateClaimed', 'expire'):
|
||||
return False
|
||||
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
args = (data, hash)
|
||||
c.execute("UPDATE hashes SET " + key + " = ? where hash = ?;", args)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True
|
97
onionr/coredb/daemonqueue/__init__.py
Normal file
97
onionr/coredb/daemonqueue/__init__.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Write and read the daemon queue, which is how messages are passed into the onionr daemon in a more
|
||||
direct way than the http api
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3, os
|
||||
import onionrevents as events
|
||||
from onionrutils import localcommand, epoch
|
||||
|
||||
def daemon_queue(core_inst):
|
||||
'''
|
||||
Gives commands to the communication proccess/daemon by reading an sqlite3 database
|
||||
|
||||
This function intended to be used by the client. Queue to exchange data between "client" and server.
|
||||
'''
|
||||
|
||||
retData = False
|
||||
if not os.path.exists(core_inst.queueDB):
|
||||
core_inst.dbCreate.createDaemonDB()
|
||||
else:
|
||||
conn = sqlite3.connect(core_inst.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
try:
|
||||
for row in c.execute('SELECT command, data, date, min(ID), responseID FROM commands group by id'):
|
||||
retData = row
|
||||
break
|
||||
except sqlite3.OperationalError:
|
||||
core_inst.dbCreate.createDaemonDB()
|
||||
else:
|
||||
if retData != False:
|
||||
c.execute('DELETE FROM commands WHERE id=?;', (retData[3],))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('queue_pop', data = {'data': retData}, onionr = core_inst.onionrInst)
|
||||
|
||||
return retData
|
||||
|
||||
def daemon_queue_add(core_inst, command, data='', responseID=''):
|
||||
'''
|
||||
Add a command to the daemon queue, used by the communication daemon (communicator.py)
|
||||
'''
|
||||
|
||||
retData = True
|
||||
|
||||
date = epoch.get_epoch()
|
||||
conn = sqlite3.connect(core_inst.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (command, data, date, responseID)
|
||||
try:
|
||||
c.execute('INSERT INTO commands (command, data, date, responseID) VALUES(?, ?, ?, ?)', t)
|
||||
conn.commit()
|
||||
except sqlite3.OperationalError:
|
||||
retData = False
|
||||
core_inst.daemonQueue()
|
||||
events.event('queue_push', data = {'command': command, 'data': data}, onionr = core_inst.onionrInst)
|
||||
conn.close()
|
||||
return retData
|
||||
|
||||
def daemon_queue_get_response(core_inst, responseID=''):
|
||||
'''
|
||||
Get a response sent by communicator to the API, by requesting to the API
|
||||
'''
|
||||
assert len(responseID) > 0
|
||||
resp = localcommand.local_command(core_inst, 'queueResponse/' + responseID)
|
||||
return resp
|
||||
|
||||
def clear_daemon_queue(core_inst):
|
||||
'''
|
||||
Clear the daemon queue (somewhat dangerous)
|
||||
'''
|
||||
conn = sqlite3.connect(core_inst.queueDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
try:
|
||||
c.execute('DELETE FROM commands;')
|
||||
conn.commit()
|
||||
except:
|
||||
pass
|
||||
|
||||
conn.close()
|
||||
events.event('queue_clear', onionr = core_inst.onionrInst)
|
1
onionr/coredb/keydb/__init__.py
Normal file
1
onionr/coredb/keydb/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from . import addkeys, listkeys, removekeys, userinfo, transportinfo
|
91
onionr/coredb/keydb/addkeys.py
Normal file
91
onionr/coredb/keydb/addkeys.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
add user keys or transport addresses
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
import onionrevents as events
|
||||
from onionrutils import stringvalidators
|
||||
|
||||
def add_peer(core_inst, peerID, name=''):
|
||||
'''
|
||||
Adds a public key to the key database (misleading function name)
|
||||
'''
|
||||
if peerID in core_inst.listPeers() or peerID == core_inst._crypto.pubKey:
|
||||
raise ValueError("specified id is already known")
|
||||
|
||||
# This function simply adds a peer to the DB
|
||||
if not stringvalidators.validate_pub_key(peerID):
|
||||
return False
|
||||
|
||||
events.event('pubkey_add', data = {'key': peerID}, onionr = core_inst.onionrInst)
|
||||
|
||||
conn = sqlite3.connect(core_inst.peerDB, timeout=30)
|
||||
hashID = core_inst._crypto.pubKeyHashID(peerID)
|
||||
c = conn.cursor()
|
||||
t = (peerID, name, 'unknown', hashID, 0)
|
||||
|
||||
for i in c.execute("SELECT * FROM peers WHERE id = ?;", (peerID,)):
|
||||
try:
|
||||
if i[0] == peerID:
|
||||
conn.close()
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
c.execute('INSERT INTO peers (id, name, dateSeen, hashID, trust) VALUES(?, ?, ?, ?, ?);', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
|
||||
def add_address(core_inst, address):
|
||||
'''
|
||||
Add an address to the address database (only tor currently)
|
||||
'''
|
||||
|
||||
if type(address) is None or len(address) == 0:
|
||||
return False
|
||||
if stringvalidators.validate_transport(address):
|
||||
if address == core_inst.config.get('i2p.ownAddr', None) or address == core_inst.hsAddress:
|
||||
return False
|
||||
conn = sqlite3.connect(core_inst.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
# check if address is in database
|
||||
# this is safe to do because the address is validated above, but we strip some chars here too just in case
|
||||
address = address.replace('\'', '').replace(';', '').replace('"', '').replace('\\', '')
|
||||
for i in c.execute("SELECT * FROM adders WHERE address = ?;", (address,)):
|
||||
try:
|
||||
if i[0] == address:
|
||||
conn.close()
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
t = (address, 1)
|
||||
c.execute('INSERT INTO adders (address, type) VALUES(?, ?);', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('address_add', data = {'address': address}, onionr = core_inst.onionrInst)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
83
onionr/coredb/keydb/listkeys.py
Normal file
83
onionr/coredb/keydb/listkeys.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
get lists for user keys or transport addresses
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
import logger
|
||||
from onionrutils import epoch
|
||||
def list_peers(core_inst, randomOrder=True, getPow=False, trust=0):
|
||||
'''
|
||||
Return a list of public keys (misleading function name)
|
||||
|
||||
randomOrder determines if the list should be in a random order
|
||||
trust sets the minimum trust to list
|
||||
'''
|
||||
conn = sqlite3.connect(core_inst.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
payload = ''
|
||||
|
||||
if trust not in (0, 1, 2):
|
||||
logger.error('Tried to select invalid trust.')
|
||||
return
|
||||
|
||||
if randomOrder:
|
||||
payload = 'SELECT * FROM peers WHERE trust >= ? ORDER BY RANDOM();'
|
||||
else:
|
||||
payload = 'SELECT * FROM peers WHERE trust >= ?;'
|
||||
|
||||
peerList = []
|
||||
|
||||
for i in c.execute(payload, (trust,)):
|
||||
try:
|
||||
if len(i[0]) != 0:
|
||||
if getPow:
|
||||
peerList.append(i[0] + '-' + i[1])
|
||||
else:
|
||||
peerList.append(i[0])
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
conn.close()
|
||||
|
||||
return peerList
|
||||
|
||||
def list_adders(core_inst, randomOrder=True, i2p=True, recent=0):
|
||||
'''
|
||||
Return a list of transport addresses
|
||||
'''
|
||||
conn = sqlite3.connect(core_inst.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
if randomOrder:
|
||||
addresses = c.execute('SELECT * FROM adders ORDER BY RANDOM();')
|
||||
else:
|
||||
addresses = c.execute('SELECT * FROM adders;')
|
||||
addressList = []
|
||||
for i in addresses:
|
||||
if len(i[0].strip()) == 0:
|
||||
continue
|
||||
addressList.append(i[0])
|
||||
conn.close()
|
||||
testList = list(addressList) # create new list to iterate
|
||||
for address in testList:
|
||||
try:
|
||||
if recent > 0 and (epoch.get_epoch() - core_inst.getAddressInfo(address, 'lastConnect')) > recent:
|
||||
raise TypeError # If there is no last-connected date or it was too long ago, don't add peer to list if recent is not 0
|
||||
except TypeError:
|
||||
addressList.remove(address)
|
||||
return addressList
|
40
onionr/coredb/keydb/removekeys.py
Normal file
40
onionr/coredb/keydb/removekeys.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Remove a transport address but don't ban them
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
import onionrevents as events
|
||||
from onionrutils import stringvalidators
|
||||
|
||||
def remove_address(core_inst, address):
|
||||
'''
|
||||
Remove an address from the address database
|
||||
'''
|
||||
|
||||
if stringvalidators.validate_transport(address):
|
||||
conn = sqlite3.connect(core_inst.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (address,)
|
||||
c.execute('Delete from adders where address=?;', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
events.event('address_remove', data = {'address': address}, onionr = core_inst.onionrInst)
|
||||
return True
|
||||
else:
|
||||
return False
|
72
onionr/coredb/keydb/transportinfo.py
Normal file
72
onionr/coredb/keydb/transportinfo.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
get or set transport address meta information
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
def get_address_info(core_inst, address, info):
|
||||
'''
|
||||
Get info about an address from its database entry
|
||||
|
||||
address text, 0
|
||||
type int, 1
|
||||
knownPeer text, 2
|
||||
speed int, 3
|
||||
success int, 4
|
||||
powValue 5
|
||||
failure int 6
|
||||
lastConnect 7
|
||||
trust 8
|
||||
introduced 9
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(core_inst.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (address,)
|
||||
infoNumbers = {'address': 0, 'type': 1, 'knownPeer': 2, 'speed': 3, 'success': 4, 'powValue': 5, 'failure': 6, 'lastConnect': 7, 'trust': 8, 'introduced': 9}
|
||||
info = infoNumbers[info]
|
||||
iterCount = 0
|
||||
retVal = ''
|
||||
|
||||
for row in c.execute('SELECT * FROM adders WHERE address=?;', command):
|
||||
for i in row:
|
||||
if iterCount == info:
|
||||
retVal = i
|
||||
break
|
||||
else:
|
||||
iterCount += 1
|
||||
conn.close()
|
||||
|
||||
return retVal
|
||||
|
||||
def set_address_info(core_inst, address, key, data):
|
||||
'''
|
||||
Update an address for a key
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(core_inst.addressDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (data, address)
|
||||
|
||||
if key not in ('address', 'type', 'knownPeer', 'speed', 'success', 'failure', 'powValue', 'lastConnect', 'lastConnectAttempt', 'trust', 'introduced'):
|
||||
raise Exception("Got invalid database key when setting address info")
|
||||
else:
|
||||
c.execute('UPDATE adders SET ' + key + ' = ? WHERE address=?', command)
|
||||
conn.commit()
|
||||
conn.close()
|
69
onionr/coredb/keydb/userinfo.py
Normal file
69
onionr/coredb/keydb/userinfo.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
get or set information about a user id
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 sqlite3
|
||||
def get_user_info(core_inst, peer, info):
|
||||
'''
|
||||
Get info about a peer from their database entry
|
||||
|
||||
id text 0
|
||||
name text, 1
|
||||
adders text, 2
|
||||
dateSeen not null, 3
|
||||
trust int 4
|
||||
hashID text 5
|
||||
'''
|
||||
conn = sqlite3.connect(core_inst.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (peer,)
|
||||
infoNumbers = {'id': 0, 'name': 1, 'adders': 2, 'dateSeen': 3, 'trust': 4, 'hashID': 5}
|
||||
info = infoNumbers[info]
|
||||
iterCount = 0
|
||||
retVal = ''
|
||||
|
||||
for row in c.execute('SELECT * FROM peers WHERE id=?;', command):
|
||||
for i in row:
|
||||
if iterCount == info:
|
||||
retVal = i
|
||||
break
|
||||
else:
|
||||
iterCount += 1
|
||||
|
||||
conn.close()
|
||||
|
||||
return retVal
|
||||
|
||||
def set_peer_info(core_inst, peer, key, data):
|
||||
'''
|
||||
Update a peer for a key
|
||||
'''
|
||||
|
||||
conn = sqlite3.connect(core_inst.peerDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
|
||||
command = (data, peer)
|
||||
|
||||
# TODO: validate key on whitelist
|
||||
if key not in ('id', 'name', 'pubkey', 'forwardKey', 'dateSeen', 'trust'):
|
||||
raise Exception("Got invalid database key when setting peer info")
|
||||
|
||||
c.execute('UPDATE peers SET ' + key + ' = ? WHERE id=?', command)
|
||||
conn.commit()
|
||||
conn.close()
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file registers plugin's flask blueprints for the client http server
|
||||
'''
|
||||
|
|
|
@ -26,7 +26,8 @@ friends = Blueprint('friends', __name__)
|
|||
@friends.route('/friends/list')
|
||||
def list_friends():
|
||||
pubkey_list = {}
|
||||
friend_list = contactmanager.ContactManager.list_friends(core.Core())
|
||||
c = core.Core()
|
||||
friend_list = contactmanager.ContactManager.list_friends(c)
|
||||
for friend in friend_list:
|
||||
pubkey_list[friend.publicKey] = {'name': friend.get_info('name')}
|
||||
return json.dumps(pubkey_list)
|
||||
|
|
|
@ -21,6 +21,8 @@ import base64
|
|||
from flask import Response
|
||||
import logger
|
||||
from etc import onionrvalues
|
||||
from onionrutils import stringvalidators, bytesconverter
|
||||
|
||||
def handle_announce(clientAPI, request):
|
||||
'''
|
||||
accept announcement posts, validating POW
|
||||
|
@ -51,8 +53,8 @@ def handle_announce(clientAPI, request):
|
|||
except AttributeError:
|
||||
pass
|
||||
if powHash.startswith('0' * onionrvalues.OnionrValues().announce_pow):
|
||||
newNode = clientAPI._core._utils.bytesToStr(newNode)
|
||||
if clientAPI._core._utils.validateID(newNode) and not newNode in clientAPI._core.onionrInst.communicatorInst.newPeers:
|
||||
newNode = bytesconverter.bytes_to_str(newNode)
|
||||
if stringvalidators.validate_transport(newNode) and not newNode in clientAPI._core.onionrInst.communicatorInst.newPeers:
|
||||
clientAPI._core.onionrInst.communicatorInst.newPeers.append(newNode)
|
||||
resp = 'Success'
|
||||
else:
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
'''
|
||||
from flask import Response, abort
|
||||
import config
|
||||
from onionrutils import bytesconverter, stringvalidators
|
||||
def get_public_block_list(clientAPI, publicAPI, request):
|
||||
# Provide a list of our blocks, with a date offset
|
||||
dateAdjust = request.args.get('date')
|
||||
bList = clientAPI._core.getBlockList(dateRec=dateAdjust)
|
||||
if config.get('general.hide_created_blocks', True):
|
||||
if clientAPI._core.config.get('general.hide_created_blocks', True):
|
||||
for b in publicAPI.hideBlocks:
|
||||
if b in bList:
|
||||
# Don't share blocks we created if they haven't been *uploaded* yet, makes it harder to find who created a block
|
||||
|
@ -33,15 +34,15 @@ def get_public_block_list(clientAPI, publicAPI, request):
|
|||
def get_block_data(clientAPI, publicAPI, data):
|
||||
'''data is the block hash in hex'''
|
||||
resp = ''
|
||||
if clientAPI._utils.validateHash(data):
|
||||
if not config.get('general.hide_created_blocks', True) or data not in publicAPI.hideBlocks:
|
||||
if stringvalidators.validate_hash(data):
|
||||
if not clientAPI._core.config.get('general.hide_created_blocks', True) or data not in publicAPI.hideBlocks:
|
||||
if data in clientAPI._core.getBlockList():
|
||||
block = clientAPI.getBlockData(data, raw=True)
|
||||
try:
|
||||
block = block.encode() # Encode in case data is binary
|
||||
except AttributeError:
|
||||
abort(404)
|
||||
block = clientAPI._core._utils.strToBytes(block)
|
||||
block = bytesconverter.str_to_bytes(block)
|
||||
resp = block
|
||||
if len(resp) == 0:
|
||||
abort(404)
|
||||
|
|
|
@ -17,20 +17,20 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
from onionrutils import bytesconverter
|
||||
import onionrcrypto
|
||||
class KeyManager:
|
||||
def __init__(self, crypto):
|
||||
assert isinstance(crypto, onionrcrypto.OnionrCrypto)
|
||||
self._core = crypto._core
|
||||
self._utils = self._core._utils
|
||||
self.keyFile = crypto._keyFile
|
||||
self.crypto = crypto
|
||||
|
||||
def addKey(self, pubKey=None, privKey=None):
|
||||
if type(pubKey) is type(None) and type(privKey) is type(None):
|
||||
pubKey, privKey = self.crypto.generatePubKey()
|
||||
pubKey = self.crypto._core._utils.bytesToStr(pubKey)
|
||||
privKey = self.crypto._core._utils.bytesToStr(privKey)
|
||||
pubKey = bytesconverter.bytes_to_str(pubKey)
|
||||
privKey = bytesconverter.bytes_to_str(privKey)
|
||||
try:
|
||||
if pubKey in self.getPubkeyList():
|
||||
raise ValueError('Pubkey already in list: %s' % (pubKey,))
|
||||
|
|
|
@ -126,24 +126,24 @@ def get_file():
|
|||
|
||||
return _outputfile
|
||||
|
||||
def raw(data, fd = sys.stdout, sensitive = False):
|
||||
def raw(data, fd = sys.stdout, terminal = False):
|
||||
'''
|
||||
Outputs raw data to console without formatting
|
||||
'''
|
||||
|
||||
if get_settings() & OUTPUT_TO_CONSOLE:
|
||||
if terminal and (get_settings() & OUTPUT_TO_CONSOLE):
|
||||
try:
|
||||
ts = fd.write('%s\n' % data)
|
||||
except OSError:
|
||||
pass
|
||||
if get_settings() & OUTPUT_TO_FILE and not sensitive:
|
||||
if get_settings() & OUTPUT_TO_FILE:
|
||||
try:
|
||||
with open(_outputfile, "a+") as f:
|
||||
f.write(colors.filter(data) + '\n')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True, sensitive = False):
|
||||
def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True, terminal = False):
|
||||
'''
|
||||
Logs the data
|
||||
prefix : The prefix to the output
|
||||
|
@ -158,7 +158,7 @@ def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True
|
|||
if not get_settings() & USE_ANSI:
|
||||
output = colors.filter(output)
|
||||
|
||||
raw(output, fd = fd, sensitive = sensitive)
|
||||
raw(output, fd = fd, terminal = terminal)
|
||||
|
||||
def readline(message = ''):
|
||||
'''
|
||||
|
@ -210,37 +210,37 @@ def confirm(default = 'y', message = 'Are you sure %s? '):
|
|||
return default == 'y'
|
||||
|
||||
# debug: when there is info that could be useful for debugging purposes only
|
||||
def debug(data, error = None, timestamp = True, prompt = True, sensitive = False, level = LEVEL_DEBUG):
|
||||
def debug(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_DEBUG):
|
||||
if get_level() <= level:
|
||||
log('/', data, timestamp = timestamp, prompt = prompt, sensitive = sensitive)
|
||||
log('/', data, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||
if not error is None:
|
||||
debug('Error: ' + str(error) + parse_error())
|
||||
|
||||
# info: when there is something to notify the user of, such as the success of a process
|
||||
def info(data, timestamp = False, prompt = True, sensitive = False, level = LEVEL_INFO):
|
||||
def info(data, timestamp = False, prompt = True, terminal = False, level = LEVEL_INFO):
|
||||
if get_level() <= level:
|
||||
log('+', data, colors.fg.green, timestamp = timestamp, prompt = prompt, sensitive = sensitive)
|
||||
log('+', data, colors.fg.green, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||
|
||||
# warn: when there is a potential for something bad to happen
|
||||
def warn(data, error = None, timestamp = True, prompt = True, sensitive = False, level = LEVEL_WARN):
|
||||
def warn(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_WARN):
|
||||
if not error is None:
|
||||
debug('Error: ' + str(error) + parse_error())
|
||||
if get_level() <= level:
|
||||
log('!', data, colors.fg.orange, timestamp = timestamp, prompt = prompt, sensitive = sensitive)
|
||||
log('!', data, colors.fg.orange, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||
|
||||
# error: when only one function, module, or process of the program encountered a problem and must stop
|
||||
def error(data, error = None, timestamp = True, prompt = True, sensitive = False, level = LEVEL_ERROR):
|
||||
def error(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_ERROR):
|
||||
if get_level() <= level:
|
||||
log('-', data, colors.fg.red, timestamp = timestamp, fd = sys.stderr, prompt = prompt, sensitive = sensitive)
|
||||
log('-', data, colors.fg.red, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
|
||||
if not error is None:
|
||||
debug('Error: ' + str(error) + parse_error())
|
||||
|
||||
# fatal: when the something so bad has happened that the program must stop
|
||||
def fatal(data, error = None, timestamp=True, prompt = True, sensitive = False, level = LEVEL_FATAL):
|
||||
def fatal(data, error = None, timestamp=True, prompt = True, terminal = False, level = LEVEL_FATAL):
|
||||
if not error is None:
|
||||
debug('Error: ' + str(error) + parse_error(), sensitive = sensitive)
|
||||
debug('Error: ' + str(error) + parse_error(), terminal = terminal)
|
||||
if get_level() <= level:
|
||||
log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp = timestamp, fd = sys.stderr, prompt = prompt, sensitive = sensitive)
|
||||
log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
|
||||
|
||||
# returns a formatted error message
|
||||
def parse_error():
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
import subprocess, os, sys, time, signal, base64, socket
|
||||
from shutil import which
|
||||
import logger, config
|
||||
from onionrblockapi import Block
|
||||
config.reload()
|
||||
def getOpenPort():
|
||||
# taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
@ -124,14 +123,14 @@ HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort)
|
|||
try:
|
||||
tor = subprocess.Popen([self.torBinary, '-f', self.torConfigLocation], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except FileNotFoundError:
|
||||
logger.fatal("Tor was not found in your path or the Onionr directory. Please install Tor and try again.")
|
||||
logger.fatal("Tor was not found in your path or the Onionr directory. Please install Tor and try again.", terminal=True)
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Test Tor Version
|
||||
torVersion = subprocess.Popen([self.torBinary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
for line in iter(torVersion.stdout.readline, b''):
|
||||
if 'Tor 0.2.' in line.decode():
|
||||
logger.error('Tor 0.3+ required')
|
||||
logger.fatal('Tor 0.3+ required', terminal=True)
|
||||
sys.exit(1)
|
||||
break
|
||||
torVersion.kill()
|
||||
|
@ -140,17 +139,18 @@ HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort)
|
|||
try:
|
||||
for line in iter(tor.stdout.readline, b''):
|
||||
if 'bootstrapped 100' in line.decode().lower():
|
||||
logger.info(line.decode())
|
||||
break
|
||||
elif 'opening socks listener' in line.decode().lower():
|
||||
logger.debug(line.decode().replace('\n', ''))
|
||||
else:
|
||||
logger.fatal('Failed to start Tor. Maybe a stray instance of Tor used by Onionr is still running? This can also be a result of file permissions being too open')
|
||||
logger.fatal('Failed to start Tor. Maybe a stray instance of Tor used by Onionr is still running? This can also be a result of file permissions being too open', terminal=True)
|
||||
return False
|
||||
except KeyboardInterrupt:
|
||||
logger.fatal('Got keyboard interrupt. Onionr will exit soon.', timestamp = False, level = logger.LEVEL_IMPORTANT)
|
||||
logger.fatal('Got keyboard interrupt. Onionr will exit soon.', timestamp = False, level = logger.LEVEL_IMPORTANT, terminal=True)
|
||||
return False
|
||||
|
||||
logger.debug('Finished starting Tor.', timestamp=True)
|
||||
logger.info('Finished starting Tor.', terminal=True)
|
||||
self.readyState = True
|
||||
|
||||
try:
|
||||
|
|
|
@ -21,14 +21,23 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import sys
|
||||
ONIONR_TAGLINE = 'Private P2P Communication - GPLv3 - https://Onionr.net'
|
||||
ONIONR_VERSION = '0.0.0' # for debugging and stuff
|
||||
ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION)
|
||||
API_VERSION = '0' # increments of 1; only change when something fundamental about how the API works changes. This way other nodes know how to communicate without learning too much information about you.
|
||||
MIN_PY_VERSION = 6
|
||||
if sys.version_info[0] == 2 or sys.version_info[1] < MIN_PY_VERSION:
|
||||
sys.stderr.write('Error, Onionr requires Python 3.%s+' % (MIN_PY_VERSION,))
|
||||
sys.stderr.write('Error, Onionr requires Python 3.%s+\n' % (MIN_PY_VERSION,))
|
||||
sys.exit(1)
|
||||
|
||||
from utils import detectoptimization
|
||||
if detectoptimization.detect_optimization():
|
||||
sys.stderr.write('Error, Onionr cannot be run in optimized mode\n')
|
||||
sys.exit(1)
|
||||
|
||||
import os, base64, random, shutil, time, platform, signal
|
||||
from threading import Thread
|
||||
import api, core, config, logger, onionrplugins as plugins, onionrevents as events
|
||||
import onionrutils
|
||||
import netcontroller
|
||||
from netcontroller import NetController
|
||||
from onionrblockapi import Block
|
||||
|
@ -40,17 +49,13 @@ try:
|
|||
except ImportError:
|
||||
raise Exception("You need the PySocks module (for use with socks5 proxy to use Tor)")
|
||||
|
||||
ONIONR_TAGLINE = 'Private P2P Communication - GPLv3 - https://Onionr.net'
|
||||
ONIONR_VERSION = '0.0.0' # for debugging and stuff
|
||||
ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION)
|
||||
API_VERSION = '0' # increments of 1; only change when something fundamental about how the API works changes. This way other nodes know how to communicate without learning too much information about you.
|
||||
|
||||
class Onionr:
|
||||
def __init__(self):
|
||||
'''
|
||||
Main Onionr class. This is for the CLI program, and does not handle much of the logic.
|
||||
In general, external programs and plugins should not use this class.
|
||||
'''
|
||||
self.API_VERSION = API_VERSION
|
||||
self.userRunDir = os.getcwd() # Directory user runs the program from
|
||||
self.killed = False
|
||||
|
||||
|
@ -72,7 +77,7 @@ class Onionr:
|
|||
data_exists = Onionr.setupConfig(self.dataDir, self)
|
||||
|
||||
if netcontroller.torBinary() is None:
|
||||
logger.error('Tor is not installed')
|
||||
logger.error('Tor is not installed', terminal=True)
|
||||
sys.exit(1)
|
||||
|
||||
# If block data folder does not exist
|
||||
|
@ -101,7 +106,6 @@ class Onionr:
|
|||
self.onionrCore = core.Core()
|
||||
self.onionrCore.onionrInst = self
|
||||
#self.deleteRunFiles()
|
||||
self.onionrUtils = onionrutils.OnionrUtils(self.onionrCore)
|
||||
|
||||
self.clientAPIInst = '' # Client http api instance
|
||||
self.publicAPIInst = '' # Public http api instance
|
||||
|
@ -159,7 +163,7 @@ class Onionr:
|
|||
sys.stderr.write(file.read().decode().replace('P', logger.colors.fg.pink).replace('W', logger.colors.reset + logger.colors.bold).replace('G', logger.colors.fg.green).replace('\n', logger.colors.reset + '\n').replace('B', logger.colors.bold).replace('A', '%s' % API_VERSION).replace('V', ONIONR_VERSION))
|
||||
|
||||
if not message is None:
|
||||
logger.info(logger.colors.fg.lightgreen + '-> ' + str(message) + logger.colors.reset + logger.colors.fg.lightgreen + ' <-\n', sensitive=True)
|
||||
logger.info(logger.colors.fg.lightgreen + '-> ' + str(message) + logger.colors.reset + logger.colors.fg.lightgreen + ' <-\n', terminal=True)
|
||||
|
||||
def deleteRunFiles(self):
|
||||
try:
|
||||
|
@ -200,7 +204,7 @@ class Onionr:
|
|||
'''
|
||||
|
||||
def exportBlock(self):
|
||||
commands.exportblocks(self)
|
||||
commands.exportblocks.export_block(self)
|
||||
|
||||
def showDetails(self):
|
||||
commands.onionrstatistics.show_details(self)
|
||||
|
@ -232,13 +236,13 @@ class Onionr:
|
|||
def listPeers(self):
|
||||
logger.info('Peer transport address list:')
|
||||
for i in self.onionrCore.listAdders():
|
||||
logger.info(i)
|
||||
logger.info(i, terminal=True)
|
||||
|
||||
def getWebPassword(self):
|
||||
return config.get('client.webpassword')
|
||||
|
||||
def printWebPassword(self):
|
||||
logger.info(self.getWebPassword(), sensitive = True)
|
||||
logger.info(self.getWebPassword(), terminal=True)
|
||||
|
||||
def getHelp(self):
|
||||
return self.cmdhelp
|
||||
|
@ -263,13 +267,13 @@ class Onionr:
|
|||
if len(sys.argv) >= 4:
|
||||
config.reload()
|
||||
config.set(sys.argv[2], sys.argv[3], True)
|
||||
logger.debug('Configuration file updated.')
|
||||
logger.info('Configuration file updated.', terminal=True)
|
||||
elif len(sys.argv) >= 3:
|
||||
config.reload()
|
||||
logger.info(logger.colors.bold + sys.argv[2] + ': ' + logger.colors.reset + str(config.get(sys.argv[2], logger.colors.fg.red + 'Not set.')))
|
||||
logger.info(logger.colors.bold + sys.argv[2] + ': ' + logger.colors.reset + str(config.get(sys.argv[2], logger.colors.fg.red + 'Not set.')), terminal=True)
|
||||
else:
|
||||
logger.info(logger.colors.bold + 'Get a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key>')
|
||||
logger.info(logger.colors.bold + 'Set a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key> <value>')
|
||||
logger.info(logger.colors.bold + 'Get a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key>', terminal=True)
|
||||
logger.info(logger.colors.bold + 'Set a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key> <value>', terminal=True)
|
||||
|
||||
def execute(self, argument):
|
||||
'''
|
||||
|
@ -289,11 +293,11 @@ class Onionr:
|
|||
Displays the Onionr version
|
||||
'''
|
||||
|
||||
function('Onionr v%s (%s) (API v%s)' % (ONIONR_VERSION, platform.machine(), API_VERSION))
|
||||
function('Onionr v%s (%s) (API v%s)' % (ONIONR_VERSION, platform.machine(), API_VERSION), terminal=True)
|
||||
if verbosity >= 1:
|
||||
function(ONIONR_TAGLINE)
|
||||
function(ONIONR_TAGLINE, terminal=True)
|
||||
if verbosity >= 2:
|
||||
function('Running on %s %s' % (platform.platform(), platform.release()))
|
||||
function('Running on %s %s' % (platform.platform(), platform.release()), terminal=True)
|
||||
|
||||
def doPEX(self):
|
||||
'''make communicator do pex'''
|
||||
|
@ -304,7 +308,7 @@ class Onionr:
|
|||
'''
|
||||
Displays a list of keys (used to be called peers) (?)
|
||||
'''
|
||||
logger.info('%sPublic keys in database: \n%s%s' % (logger.colors.fg.lightgreen, logger.colors.fg.green, '\n'.join(self.onionrCore.listPeers())))
|
||||
logger.info('%sPublic keys in database: \n%s%s' % (logger.colors.fg.lightgreen, logger.colors.fg.green, '\n'.join(self.onionrCore.listPeers())), terminal=True)
|
||||
|
||||
def addPeer(self):
|
||||
'''
|
||||
|
@ -347,14 +351,14 @@ class Onionr:
|
|||
Displays a "command not found" message
|
||||
'''
|
||||
|
||||
logger.error('Command not found.', timestamp = False)
|
||||
logger.error('Command not found.', timestamp = False, terminal=True)
|
||||
|
||||
def showHelpSuggestion(self):
|
||||
'''
|
||||
Displays a message suggesting help
|
||||
'''
|
||||
if __name__ == '__main__':
|
||||
logger.info('Do ' + logger.colors.bold + sys.argv[0] + ' --help' + logger.colors.reset + logger.colors.fg.green + ' for Onionr help.')
|
||||
logger.info('Do ' + logger.colors.bold + sys.argv[0] + ' --help' + logger.colors.reset + logger.colors.fg.green + ' for Onionr help.', terminal=True)
|
||||
|
||||
def start(self, input = False, override = False):
|
||||
'''
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import sqlite3, os, logger
|
||||
from onionrutils import epoch, bytesconverter
|
||||
class OnionrBlackList:
|
||||
def __init__(self, coreInst):
|
||||
self.blacklistDB = coreInst.dataDir + 'blacklist.db'
|
||||
|
@ -28,7 +29,7 @@ class OnionrBlackList:
|
|||
return
|
||||
|
||||
def inBlacklist(self, data):
|
||||
hashed = self._core._utils.bytesToStr(self._core._crypto.sha3Hash(data))
|
||||
hashed = bytesconverter.bytes_to_str(self._core._crypto.sha3Hash(data))
|
||||
retData = False
|
||||
|
||||
if not hashed.isalnum():
|
||||
|
@ -56,7 +57,7 @@ class OnionrBlackList:
|
|||
def deleteExpired(self, dataType=0):
|
||||
'''Delete expired entries'''
|
||||
deleteList = []
|
||||
curTime = self._core._utils.getEpoch()
|
||||
curTime = epoch.get_epoch()
|
||||
|
||||
try:
|
||||
int(dataType)
|
||||
|
@ -98,7 +99,7 @@ class OnionrBlackList:
|
|||
2=pubkey
|
||||
'''
|
||||
# we hash the data so we can remove data entirely from our node's disk
|
||||
hashed = self._core._utils.bytesToStr(self._core._crypto.sha3Hash(data))
|
||||
hashed = bytesconverter.bytes_to_str(self._core._crypto.sha3Hash(data))
|
||||
if len(hashed) > 64:
|
||||
raise Exception("Hashed data is too large")
|
||||
|
||||
|
@ -115,7 +116,7 @@ class OnionrBlackList:
|
|||
if self.inBlacklist(hashed):
|
||||
return
|
||||
insert = (hashed,)
|
||||
blacklistDate = self._core._utils.getEpoch()
|
||||
blacklistDate = epoch.get_epoch()
|
||||
try:
|
||||
self._dbExecute("INSERT INTO blacklist (hash, dataType, blacklistDate, expire) VALUES(?, ?, ?, ?);", (str(hashed), dataType, blacklistDate, expire))
|
||||
except sqlite3.IntegrityError:
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
import core as onionrcore, logger, config, onionrexceptions, nacl.exceptions
|
||||
import json, os, sys, datetime, base64, onionrstorage
|
||||
from onionrusers import onionrusers
|
||||
from onionrutils import stringvalidators, epoch
|
||||
|
||||
class Block:
|
||||
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
||||
|
@ -88,7 +89,7 @@ class Block:
|
|||
|
||||
# Check for replay attacks
|
||||
try:
|
||||
if self.core._utils.getEpoch() - self.core.getBlockDate(self.hash) < 60:
|
||||
if epoch.get_epoch() - self.core.getBlockDate(self.hash) < 60:
|
||||
assert self.core._crypto.replayTimestampValidation(self.bmetadata['rply'])
|
||||
except (AssertionError, KeyError, TypeError) as e:
|
||||
if not self.bypassReplayCheck:
|
||||
|
@ -441,7 +442,7 @@ class Block:
|
|||
'''
|
||||
|
||||
try:
|
||||
if (not self.isSigned()) or (not self.getCore()._utils.validatePubKey(signer)):
|
||||
if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
|
||||
return False
|
||||
|
||||
return bool(self.getCore()._crypto.edVerify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
|
||||
|
|
|
@ -22,6 +22,7 @@ import webbrowser, sys
|
|||
import logger
|
||||
from . import pubkeymanager, onionrstatistics, daemonlaunch, filecommands, plugincommands, keyadders
|
||||
from . import banblocks, exportblocks, openwebinterface, resettor
|
||||
from onionrutils import importnewblocks
|
||||
|
||||
def show_help(o_inst, command):
|
||||
|
||||
|
@ -110,8 +111,8 @@ def get_commands(onionr_inst):
|
|||
'listconn': onionr_inst.listConn,
|
||||
'list-conn': onionr_inst.listConn,
|
||||
|
||||
'import-blocks': onionr_inst.onionrUtils.importNewBlocks,
|
||||
'importblocks': onionr_inst.onionrUtils.importNewBlocks,
|
||||
'import-blocks': importnewblocks.import_new_blocks,
|
||||
'importblocks': importnewblocks.import_new_blocks,
|
||||
|
||||
'introduce': onionr_inst.onionrCore.introduceNode,
|
||||
'pex': onionr_inst.doPEX,
|
||||
|
|
|
@ -19,21 +19,22 @@
|
|||
'''
|
||||
import sys
|
||||
import logger
|
||||
from onionrutils import stringvalidators
|
||||
def ban_block(o_inst):
|
||||
try:
|
||||
ban = sys.argv[2]
|
||||
except IndexError:
|
||||
ban = logger.readline('Enter a block hash:')
|
||||
if o_inst.onionrUtils.validateHash(ban):
|
||||
if stringvalidators.validate_hash(ban):
|
||||
if not o_inst.onionrCore._blacklist.inBlacklist(ban):
|
||||
try:
|
||||
o_inst.onionrCore._blacklist.addToDB(ban)
|
||||
o_inst.onionrCore.removeBlock(ban)
|
||||
except Exception as error:
|
||||
logger.error('Could not blacklist block', error=error)
|
||||
logger.error('Could not blacklist block', error=error, terminal=True)
|
||||
else:
|
||||
logger.info('Block blacklisted')
|
||||
logger.info('Block blacklisted', terminal=True)
|
||||
else:
|
||||
logger.warn('That block is already blacklisted')
|
||||
logger.warn('That block is already blacklisted', terminal=True)
|
||||
else:
|
||||
logger.error('Invalid block hash')
|
||||
logger.error('Invalid block hash', terminal=True)
|
|
@ -23,9 +23,10 @@ from threading import Thread
|
|||
import onionr, api, logger, communicator
|
||||
import onionrevents as events
|
||||
from netcontroller import NetController
|
||||
from onionrutils import localcommand
|
||||
|
||||
def _proper_shutdown(o_inst):
|
||||
o_inst.onionrUtils.localCommand('shutdown')
|
||||
localcommand.local_command(o_inst.onionrCore, 'shutdown')
|
||||
sys.exit(1)
|
||||
|
||||
def daemon(o_inst):
|
||||
|
@ -38,13 +39,8 @@ def daemon(o_inst):
|
|||
logger.debug('Runcheck file found on daemon start, deleting in advance.')
|
||||
os.remove('%s/.runcheck' % (o_inst.onionrCore.dataDir,))
|
||||
|
||||
Thread(target=api.API, args=(o_inst, o_inst.debug, onionr.API_VERSION)).start()
|
||||
Thread(target=api.PublicAPI, args=[o_inst.getClientApi()]).start()
|
||||
try:
|
||||
time.sleep(0)
|
||||
except KeyboardInterrupt:
|
||||
logger.debug('Got keyboard interrupt, shutting down...')
|
||||
_proper_shutdown(o_inst)
|
||||
Thread(target=api.API, args=(o_inst, o_inst.debug, onionr.API_VERSION), daemon=True).start()
|
||||
Thread(target=api.PublicAPI, args=[o_inst.getClientApi()], daemon=True).start()
|
||||
|
||||
apiHost = ''
|
||||
while apiHost == '':
|
||||
|
@ -56,18 +52,25 @@ def daemon(o_inst):
|
|||
time.sleep(0.5)
|
||||
#onionr.Onionr.setupConfig('data/', self = o_inst)
|
||||
|
||||
logger.raw('', terminal=True)
|
||||
# print nice header thing :)
|
||||
if o_inst.onionrCore.config.get('general.display_header', True):
|
||||
o_inst.header()
|
||||
o_inst.version(verbosity = 5, function = logger.info)
|
||||
logger.debug('Python version %s' % platform.python_version())
|
||||
|
||||
if o_inst._developmentMode:
|
||||
logger.warn('DEVELOPMENT MODE ENABLED (NOT RECOMMENDED)', timestamp = False)
|
||||
logger.warn('Development mode enabled', timestamp = False, terminal=True)
|
||||
net = NetController(o_inst.onionrCore.config.get('client.public.port', 59497), apiServerIP=apiHost)
|
||||
logger.debug('Tor is starting...')
|
||||
logger.info('Tor is starting...', terminal=True)
|
||||
if not net.startTor():
|
||||
o_inst.onionrUtils.localCommand('shutdown')
|
||||
localcommand.local_command(o_inst.onionrCore, 'shutdown')
|
||||
sys.exit(1)
|
||||
if len(net.myID) > 0 and o_inst.onionrCore.config.get('general.security_level', 1) == 0:
|
||||
logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID))
|
||||
else:
|
||||
logger.debug('.onion service disabled')
|
||||
logger.debug('Using public key: %s' % (logger.colors.underline + o_inst.onionrCore._crypto.pubKey))
|
||||
logger.info('Using public key: %s' % (logger.colors.underline + o_inst.onionrCore._crypto.pubKey[:52]), terminal=True)
|
||||
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
@ -75,20 +78,12 @@ def daemon(o_inst):
|
|||
_proper_shutdown(o_inst)
|
||||
|
||||
o_inst.onionrCore.torPort = net.socksPort
|
||||
communicatorThread = Thread(target=communicator.startCommunicator, args=(o_inst, str(net.socksPort)))
|
||||
communicatorThread = Thread(target=communicator.startCommunicator, args=(o_inst, str(net.socksPort)), daemon=True)
|
||||
communicatorThread.start()
|
||||
|
||||
while o_inst.communicatorInst is None:
|
||||
time.sleep(0.1)
|
||||
|
||||
# print nice header thing :)
|
||||
if o_inst.onionrCore.config.get('general.display_header', True):
|
||||
o_inst.header()
|
||||
|
||||
# print out debug info
|
||||
o_inst.version(verbosity = 5, function = logger.debug)
|
||||
logger.debug('Python version %s' % platform.python_version())
|
||||
|
||||
logger.debug('Started communicator.')
|
||||
|
||||
events.event('daemon_start', onionr = o_inst)
|
||||
|
@ -109,10 +104,10 @@ def daemon(o_inst):
|
|||
|
||||
signal.signal(signal.SIGINT, _ignore_sigint)
|
||||
o_inst.onionrCore.daemonQueueAdd('shutdown')
|
||||
o_inst.onionrUtils.localCommand('shutdown')
|
||||
localcommand.local_command(o_inst.onionrCore, 'shutdown')
|
||||
|
||||
net.killTor()
|
||||
time.sleep(3)
|
||||
time.sleep(5) # Time to allow threads to finish, if not any "daemon" threads will be slaughtered http://docs.python.org/library/threading.html#threading.Thread.daemon
|
||||
o_inst.deleteRunFiles()
|
||||
return
|
||||
|
||||
|
@ -124,7 +119,7 @@ def kill_daemon(o_inst):
|
|||
Shutdown the Onionr daemon
|
||||
'''
|
||||
|
||||
logger.warn('Stopping the running daemon...', timestamp = False)
|
||||
logger.warn('Stopping the running daemon...', timestamp = False, terminal=True)
|
||||
try:
|
||||
events.event('daemon_stop', onionr = o_inst)
|
||||
net = NetController(o_inst.onionrCore.config.get('client.port', 59496))
|
||||
|
@ -135,12 +130,12 @@ def kill_daemon(o_inst):
|
|||
|
||||
net.killTor()
|
||||
except Exception as e:
|
||||
logger.error('Failed to shutdown daemon.', error = e, timestamp = False)
|
||||
logger.error('Failed to shutdown daemon.', error = e, timestamp = False, terminal=True)
|
||||
return
|
||||
|
||||
def start(o_inst, input = False, override = False):
|
||||
if os.path.exists('.onionr-lock') and not override:
|
||||
logger.fatal('Cannot start. Daemon is already running, or it did not exit cleanly.\n(if you are sure that there is not a daemon running, delete .onionr-lock & try again).')
|
||||
logger.fatal('Cannot start. Daemon is already running, or it did not exit cleanly.\n(if you are sure that there is not a daemon running, delete .onionr-lock & try again).', terminal=True)
|
||||
else:
|
||||
if not o_inst.debug and not o_inst._developmentMode:
|
||||
lockFile = open('.onionr-lock', 'w')
|
||||
|
|
|
@ -17,26 +17,28 @@
|
|||
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
|
||||
import sys, os
|
||||
import logger, onionrstorage
|
||||
from onionrutils import stringvalidators
|
||||
def doExport(o_inst, bHash):
|
||||
exportDir = o_inst.dataDir + 'block-export/'
|
||||
if not os.path.exists(exportDir):
|
||||
if os.path.exists(o_inst.dataDir):
|
||||
os.mkdir(exportDir)
|
||||
else:
|
||||
logger.error('Onionr Not initialized')
|
||||
logger.error('Onionr Not initialized', terminal=True)
|
||||
data = onionrstorage.getData(o_inst.onionrCore, bHash)
|
||||
with open('%s/%s.dat' % (exportDir, bHash), 'wb') as exportFile:
|
||||
exportFile.write(data)
|
||||
logger.info('Block exported as file', terminal=True)
|
||||
|
||||
def export_block(o_inst):
|
||||
exportDir = o_inst.dataDir + 'block-export/'
|
||||
try:
|
||||
assert o_inst.onionrUtils.validateHash(sys.argv[2])
|
||||
assert stringvalidators.validate_hash(sys.argv[2])
|
||||
except (IndexError, AssertionError):
|
||||
logger.error('No valid block hash specified.')
|
||||
logger.error('No valid block hash specified.', terminal=True)
|
||||
sys.exit(1)
|
||||
else:
|
||||
bHash = sys.argv[2]
|
||||
o_inst.doExport(bHash)
|
||||
doExport(o_inst, bHash)
|
|
@ -21,6 +21,7 @@
|
|||
import base64, sys, os
|
||||
import logger
|
||||
from onionrblockapi import Block
|
||||
from onionrutils import stringvalidators
|
||||
def add_file(o_inst, singleBlock=False, blockType='bin'):
|
||||
'''
|
||||
Adds a file to the onionr network
|
||||
|
@ -31,18 +32,18 @@ def add_file(o_inst, singleBlock=False, blockType='bin'):
|
|||
contents = None
|
||||
|
||||
if not os.path.exists(filename):
|
||||
logger.error('That file does not exist. Improper path (specify full path)?')
|
||||
logger.error('That file does not exist. Improper path (specify full path)?', terminal=True)
|
||||
return
|
||||
logger.info('Adding file... this might take a long time.')
|
||||
logger.info('Adding file... this might take a long time.', terminal=True)
|
||||
try:
|
||||
with open(filename, 'rb') as singleFile:
|
||||
blockhash = o_inst.onionrCore.insertBlock(base64.b64encode(singleFile.read()), header=blockType)
|
||||
if len(blockhash) > 0:
|
||||
logger.info('File %s saved in block %s' % (filename, blockhash))
|
||||
logger.info('File %s saved in block %s' % (filename, blockhash), terminal=True)
|
||||
except:
|
||||
logger.error('Failed to save file in block.', timestamp = False)
|
||||
logger.error('Failed to save file in block.', timestamp = False, terminal=True)
|
||||
else:
|
||||
logger.error('%s add-file <filename>' % sys.argv[0], timestamp = False)
|
||||
logger.error('%s add-file <filename>' % sys.argv[0], timestamp = False, terminal=True)
|
||||
|
||||
def getFile(o_inst):
|
||||
'''
|
||||
|
@ -52,16 +53,16 @@ def getFile(o_inst):
|
|||
fileName = sys.argv[2]
|
||||
bHash = sys.argv[3]
|
||||
except IndexError:
|
||||
logger.error("Syntax %s %s" % (sys.argv[0], '/path/to/filename <blockhash>'))
|
||||
logger.error("Syntax %s %s" % (sys.argv[0], '/path/to/filename <blockhash>'), terminal=True)
|
||||
else:
|
||||
logger.info(fileName)
|
||||
logger.info(fileName, terminal=True)
|
||||
|
||||
contents = None
|
||||
if os.path.exists(fileName):
|
||||
logger.error("File already exists")
|
||||
logger.error("File already exists", terminal=True)
|
||||
return
|
||||
if not o_inst.onionrUtils.validateHash(bHash):
|
||||
logger.error('Block hash is invalid')
|
||||
if not stringvalidators.validate_hash(bHash):
|
||||
logger.error('Block hash is invalid', terminal=True)
|
||||
return
|
||||
|
||||
with open(fileName, 'wb') as myFile:
|
||||
|
|
|
@ -25,15 +25,15 @@ def add_peer(o_inst):
|
|||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if o_inst.onionrUtils.hasKey(newPeer):
|
||||
logger.info('We already have that key')
|
||||
if newPeer in o_inst.onionrCore.listPeers():
|
||||
logger.info('We already have that key', terminal=True)
|
||||
return
|
||||
logger.info("Adding peer: " + logger.colors.underline + newPeer)
|
||||
logger.info("Adding peer: " + logger.colors.underline + newPeer, terminal=True)
|
||||
try:
|
||||
if o_inst.onionrCore.addPeer(newPeer):
|
||||
logger.info('Successfully added key')
|
||||
logger.info('Successfully added key', terminal=True)
|
||||
except AssertionError:
|
||||
logger.error('Failed to add key')
|
||||
logger.error('Failed to add key', terminal=True)
|
||||
|
||||
def add_address(o_inst):
|
||||
try:
|
||||
|
@ -42,8 +42,8 @@ def add_address(o_inst):
|
|||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
logger.info("Adding address: " + logger.colors.underline + newAddress)
|
||||
logger.info("Adding address: " + logger.colors.underline + newAddress, terminal=True)
|
||||
if o_inst.onionrCore.addAddress(newAddress):
|
||||
logger.info("Successfully added address.")
|
||||
logger.info("Successfully added address.", terminal=True)
|
||||
else:
|
||||
logger.warn("Unable to add address.")
|
||||
logger.warn("Unable to add address.", terminal=True)
|
|
@ -18,9 +18,11 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import os, uuid, time
|
||||
import logger, onionrutils
|
||||
import logger
|
||||
from onionrblockapi import Block
|
||||
import onionr
|
||||
from onionrutils import checkcommunicator, mnemonickeys
|
||||
from utils import sizeutils
|
||||
|
||||
def show_stats(o_inst):
|
||||
try:
|
||||
|
@ -29,13 +31,13 @@ def show_stats(o_inst):
|
|||
signedBlocks = len(Block.getBlocks(signed = True))
|
||||
messages = {
|
||||
# info about local client
|
||||
'Onionr Daemon Status' : ((logger.colors.fg.green + 'Online') if o_inst.onionrUtils.isCommunicatorRunning(timeout = 9) else logger.colors.fg.red + 'Offline'),
|
||||
'Onionr Daemon Status' : ((logger.colors.fg.green + 'Online') if checkcommunicator.is_communicator_running(o_inst.onionrCore, timeout = 9) else logger.colors.fg.red + 'Offline'),
|
||||
|
||||
# file and folder size stats
|
||||
'div1' : True, # this creates a solid line across the screen, a div
|
||||
'Total Block Size' : onionrutils.humanSize(onionrutils.size(o_inst.dataDir + 'blocks/')),
|
||||
'Total Plugin Size' : onionrutils.humanSize(onionrutils.size(o_inst.dataDir + 'plugins/')),
|
||||
'Log File Size' : onionrutils.humanSize(onionrutils.size(o_inst.dataDir + 'output.log')),
|
||||
'Total Block Size' : sizeutils.human_size(sizeutils.size(o_inst.dataDir + 'blocks/')),
|
||||
'Total Plugin Size' : sizeutils.human_size(sizeutils.size(o_inst.dataDir + 'plugins/')),
|
||||
'Log File Size' : sizeutils.human_size(sizeutils.size(o_inst.dataDir + 'output.log')),
|
||||
|
||||
# count stats
|
||||
'div2' : True,
|
||||
|
@ -65,32 +67,32 @@ def show_stats(o_inst):
|
|||
groupsize = width - prewidth - len('[+] ')
|
||||
|
||||
# generate stats table
|
||||
logger.info(colors['title'] + 'Onionr v%s Statistics' % onionr.ONIONR_VERSION + colors['reset'])
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'])
|
||||
logger.info(colors['title'] + 'Onionr v%s Statistics' % onionr.ONIONR_VERSION + colors['reset'], terminal=True)
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'], terminal=True)
|
||||
for key, val in messages.items():
|
||||
if not (type(val) is bool and val is True):
|
||||
val = [str(val)[i:i + groupsize] for i in range(0, len(str(val)), groupsize)]
|
||||
|
||||
logger.info(colors['key'] + str(key).rjust(maxlength) + colors['reset'] + colors['border'] + ' | ' + colors['reset'] + colors['val'] + str(val.pop(0)) + colors['reset'])
|
||||
logger.info(colors['key'] + str(key).rjust(maxlength) + colors['reset'] + colors['border'] + ' | ' + colors['reset'] + colors['val'] + str(val.pop(0)) + colors['reset'], terminal=True)
|
||||
|
||||
for value in val:
|
||||
logger.info(' ' * maxlength + colors['border'] + ' | ' + colors['reset'] + colors['val'] + str(value) + colors['reset'])
|
||||
logger.info(' ' * maxlength + colors['border'] + ' | ' + colors['reset'] + colors['val'] + str(value) + colors['reset'], terminal=True)
|
||||
else:
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'])
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'])
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'], terminal=True)
|
||||
logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset'], terminal=True)
|
||||
except Exception as e:
|
||||
logger.error('Failed to generate statistics table.', error = e, timestamp = False)
|
||||
logger.error('Failed to generate statistics table. ' + str(e), error = e, timestamp = False, terminal=True)
|
||||
|
||||
def show_details(o_inst):
|
||||
details = {
|
||||
'Node Address' : o_inst.get_hostname(),
|
||||
'Web Password' : o_inst.getWebPassword(),
|
||||
'Public Key' : o_inst.onionrCore._crypto.pubKey,
|
||||
'Human-readable Public Key' : o_inst.onionrCore._utils.getHumanReadableID()
|
||||
'Human-readable Public Key' : mnemonickeys.get_human_readable_ID(o_inst.onionrCore)
|
||||
}
|
||||
|
||||
for detail in details:
|
||||
logger.info('%s%s: \n%s%s\n' % (logger.colors.fg.lightgreen, detail, logger.colors.fg.green, details[detail]), sensitive = True)
|
||||
logger.info('%s%s: \n%s%s\n' % (logger.colors.fg.lightgreen, detail, logger.colors.fg.green, details[detail]), terminal = True)
|
||||
|
||||
def show_peers(o_inst):
|
||||
randID = str(uuid.uuid4())
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
'''
|
||||
import webbrowser
|
||||
import logger
|
||||
from onionrutils import getclientapiserver
|
||||
def open_home(o_inst):
|
||||
try:
|
||||
url = o_inst.onionrUtils.getClientAPIServer()
|
||||
url = getclientapiserver.get_client_API_server(o_inst.onionrCore)
|
||||
except FileNotFoundError:
|
||||
logger.error('Onionr seems to not be running (could not get api host)')
|
||||
logger.error('Onionr seems to not be running (could not get api host)', terminal=True)
|
||||
else:
|
||||
url = 'http://%s/#%s' % (url, o_inst.onionrCore.config.get('client.webpassword'))
|
||||
logger.info('If Onionr does not open automatically, use this URL: ' + url)
|
||||
logger.info('If Onionr does not open automatically, use this URL: ' + url, terminal=True)
|
||||
webbrowser.open_new_tab(url)
|
|
@ -24,18 +24,18 @@ import logger, onionrplugins as plugins
|
|||
def enable_plugin(o_inst):
|
||||
if len(sys.argv) >= 3:
|
||||
plugin_name = sys.argv[2]
|
||||
logger.info('Enabling plugin "%s"...' % plugin_name)
|
||||
logger.info('Enabling plugin "%s"...' % plugin_name, terminal=True)
|
||||
plugins.enable(plugin_name, o_inst)
|
||||
else:
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]))
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]), terminal=True)
|
||||
|
||||
def disable_plugin(o_inst):
|
||||
if len(sys.argv) >= 3:
|
||||
plugin_name = sys.argv[2]
|
||||
logger.info('Disabling plugin "%s"...' % plugin_name)
|
||||
logger.info('Disabling plugin "%s"...' % plugin_name, terminal=True)
|
||||
plugins.disable(plugin_name, o_inst)
|
||||
else:
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]))
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]), terminal=True)
|
||||
|
||||
def reload_plugin(o_inst):
|
||||
'''
|
||||
|
@ -44,11 +44,11 @@ def reload_plugin(o_inst):
|
|||
|
||||
if len(sys.argv) >= 3:
|
||||
plugin_name = sys.argv[2]
|
||||
logger.info('Reloading plugin "%s"...' % plugin_name)
|
||||
logger.info('Reloading plugin "%s"...' % plugin_name, terminal=True)
|
||||
plugins.stop(plugin_name, o_inst)
|
||||
plugins.start(plugin_name, o_inst)
|
||||
else:
|
||||
logger.info('Reloading all plugins...')
|
||||
logger.info('Reloading all plugins...', terminal=True)
|
||||
plugins.reload(o_inst)
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ def create_plugin(o_inst):
|
|||
plugin_name = re.sub('[^0-9a-zA-Z_]+', '', str(sys.argv[2]).lower())
|
||||
|
||||
if not plugins.exists(plugin_name):
|
||||
logger.info('Creating plugin "%s"...' % plugin_name)
|
||||
logger.info('Creating plugin "%s"...' % plugin_name, terminal=True)
|
||||
|
||||
os.makedirs(plugins.get_plugins_folder(plugin_name))
|
||||
with open(plugins.get_plugins_folder(plugin_name) + '/main.py', 'a') as main:
|
||||
|
@ -76,12 +76,12 @@ def create_plugin(o_inst):
|
|||
with open(plugins.get_plugins_folder(plugin_name) + '/info.json', 'a') as main:
|
||||
main.write(json.dumps({'author' : 'anonymous', 'description' : 'the default description of the plugin', 'version' : '1.0'}))
|
||||
|
||||
logger.info('Enabling plugin "%s"...' % plugin_name)
|
||||
logger.info('Enabling plugin "%s"...' % plugin_name, terminal=True)
|
||||
plugins.enable(plugin_name, o_inst)
|
||||
else:
|
||||
logger.warn('Cannot create plugin directory structure; plugin "%s" exists.' % plugin_name)
|
||||
logger.warn('Cannot create plugin directory structure; plugin "%s" exists.' % plugin_name, terminal=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error('Failed to create plugin directory structure.', e)
|
||||
logger.error('Failed to create plugin directory structure.', e, terminal=True)
|
||||
else:
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]))
|
||||
logger.info('%s %s <plugin>' % (sys.argv[0], sys.argv[1]), terminal=True)
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
import sys, getpass
|
||||
import logger, onionrexceptions
|
||||
from onionrutils import stringvalidators, bytesconverter
|
||||
from onionrusers import onionrusers, contactmanager
|
||||
import unpaddedbase32
|
||||
def add_ID(o_inst):
|
||||
try:
|
||||
sys.argv[2]
|
||||
|
@ -28,41 +30,46 @@ def add_ID(o_inst):
|
|||
except (IndexError, AssertionError) as e:
|
||||
newID = o_inst.onionrCore._crypto.keyManager.addKey()[0]
|
||||
else:
|
||||
logger.warn('Deterministic keys require random and long passphrases.')
|
||||
logger.warn('If a good passphrase is not used, your key can be easily stolen.')
|
||||
logger.warn('You should use a series of hard to guess words, see this for reference: https://www.xkcd.com/936/')
|
||||
logger.warn('Deterministic keys require random and long passphrases.', terminal=True)
|
||||
logger.warn('If a good passphrase is not used, your key can be easily stolen.', terminal=True)
|
||||
logger.warn('You should use a series of hard to guess words, see this for reference: https://www.xkcd.com/936/', terminal=True)
|
||||
pass1 = getpass.getpass(prompt='Enter at least %s characters: ' % (o_inst.onionrCore._crypto.deterministicRequirement,))
|
||||
pass2 = getpass.getpass(prompt='Confirm entry: ')
|
||||
if o_inst.onionrCore._crypto.safeCompare(pass1, pass2):
|
||||
try:
|
||||
logger.info('Generating deterministic key. This can take a while.')
|
||||
logger.info('Generating deterministic key. This can take a while.', terminal=True)
|
||||
newID, privKey = o_inst.onionrCore._crypto.generateDeterministic(pass1)
|
||||
except onionrexceptions.PasswordStrengthError:
|
||||
logger.error('Passphrase must use at least %s characters.' % (o_inst.onionrCore._crypto.deterministicRequirement,))
|
||||
logger.error('Passphrase must use at least %s characters.' % (o_inst.onionrCore._crypto.deterministicRequirement,), terminal=True)
|
||||
sys.exit(1)
|
||||
else:
|
||||
logger.error('Passwords do not match.')
|
||||
logger.error('Passwords do not match.', terminal=True)
|
||||
sys.exit(1)
|
||||
o_inst.onionrCore._crypto.keyManager.addKey(pubKey=newID,
|
||||
privKey=privKey)
|
||||
logger.info('Added ID: %s' % (o_inst.onionrUtils.bytesToStr(newID),))
|
||||
try:
|
||||
o_inst.onionrCore._crypto.keyManager.addKey(pubKey=newID,
|
||||
privKey=privKey)
|
||||
except ValueError:
|
||||
logger.error('That ID is already available, you can change to it with the change-id command.', terminal=True)
|
||||
return
|
||||
logger.info('Added ID: %s' % (bytesconverter.bytes_to_str(newID),), terminal=True)
|
||||
|
||||
def change_ID(o_inst):
|
||||
try:
|
||||
key = sys.argv[2]
|
||||
key = unpaddedbase32.repad(key.encode()).decode()
|
||||
except IndexError:
|
||||
logger.warn('Specify pubkey to use')
|
||||
logger.warn('Specify pubkey to use', terminal=True)
|
||||
else:
|
||||
if o_inst.onionrUtils.validatePubKey(key):
|
||||
if stringvalidators.validate_pub_key(key):
|
||||
if key in o_inst.onionrCore._crypto.keyManager.getPubkeyList():
|
||||
o_inst.onionrCore.config.set('general.public_key', key)
|
||||
o_inst.onionrCore.config.save()
|
||||
logger.info('Set active key to: %s' % (key,))
|
||||
logger.info('Restart Onionr if it is running.')
|
||||
logger.info('Set active key to: %s' % (key,), terminal=True)
|
||||
logger.info('Restart Onionr if it is running.', terminal=True)
|
||||
else:
|
||||
logger.warn('That key does not exist')
|
||||
logger.warn('That key does not exist', terminal=True)
|
||||
else:
|
||||
logger.warn('Invalid key %s' % (key,))
|
||||
logger.warn('Invalid key %s' % (key,), terminal=True)
|
||||
|
||||
def friend_command(o_inst):
|
||||
friend = ''
|
||||
|
@ -70,23 +77,23 @@ def friend_command(o_inst):
|
|||
# Get the friend command
|
||||
action = sys.argv[2]
|
||||
except IndexError:
|
||||
logger.info('Syntax: friend add/remove/list [address]')
|
||||
logger.info('Syntax: friend add/remove/list [address]', terminal=True)
|
||||
else:
|
||||
action = action.lower()
|
||||
if action == 'list':
|
||||
# List out peers marked as our friend
|
||||
for friend in contactmanager.ContactManager.list_friends(o_inst.onionrCore):
|
||||
logger.info(friend.publicKey + ' - ' + friend.get_info('name'))
|
||||
logger.info(friend.publicKey + ' - ' + friend.get_info('name'), terminal=True)
|
||||
elif action in ('add', 'remove'):
|
||||
try:
|
||||
friend = sys.argv[3]
|
||||
if not o_inst.onionrUtils.validatePubKey(friend):
|
||||
if not stringvalidators.validate_pub_key(friend):
|
||||
raise onionrexceptions.InvalidPubkey('Public key is invalid')
|
||||
if friend not in o_inst.onionrCore.listPeers():
|
||||
raise onionrexceptions.KeyNotKnown
|
||||
friend = onionrusers.OnionrUser(o_inst.onionrCore, friend)
|
||||
except IndexError:
|
||||
logger.warn('Friend ID is required.')
|
||||
logger.warn('Friend ID is required.', terminal=True)
|
||||
action = 'error' # set to 'error' so that the finally block does not process anything
|
||||
except onionrexceptions.KeyNotKnown:
|
||||
o_inst.onionrCore.addPeer(friend)
|
||||
|
@ -94,9 +101,9 @@ def friend_command(o_inst):
|
|||
finally:
|
||||
if action == 'add':
|
||||
friend.setTrust(1)
|
||||
logger.info('Added %s as friend.' % (friend.publicKey,))
|
||||
logger.info('Added %s as friend.' % (friend.publicKey,), terminal=True)
|
||||
elif action == 'remove':
|
||||
friend.setTrust(0)
|
||||
logger.info('Removed %s as friend.' % (friend.publicKey,))
|
||||
logger.info('Removed %s as friend.' % (friend.publicKey,), terminal=True)
|
||||
else:
|
||||
logger.info('Syntax: friend add/remove/list [address]')
|
||||
logger.info('Syntax: friend add/remove/list [address]', terminal=True)
|
|
@ -19,11 +19,13 @@
|
|||
'''
|
||||
import os, shutil
|
||||
import logger, core
|
||||
from onionrutils import localcommand
|
||||
|
||||
def reset_tor():
|
||||
c = core.Core()
|
||||
tor_dir = c.dataDir + 'tordata'
|
||||
if os.path.exists(tor_dir):
|
||||
if c._utils.localCommand('/ping') == 'pong!':
|
||||
logger.warn('Cannot delete Tor data while Onionr is running')
|
||||
if localcommand.local_command(c, '/ping') == 'pong!':
|
||||
logger.warn('Cannot delete Tor data while Onionr is running', terminal=True)
|
||||
else:
|
||||
shutil.rmtree(tor_dir)
|
|
@ -19,8 +19,10 @@
|
|||
'''
|
||||
import os, binascii, base64, hashlib, time, sys, hmac, secrets
|
||||
import nacl.signing, nacl.encoding, nacl.public, nacl.hash, nacl.pwhash, nacl.utils, nacl.secret
|
||||
import unpaddedbase32
|
||||
import logger, onionrproofs
|
||||
import onionrexceptions, keymanager, core
|
||||
from onionrutils import stringvalidators, epoch, bytesconverter
|
||||
import onionrexceptions, keymanager, core, onionrutils
|
||||
import config
|
||||
config.reload()
|
||||
|
||||
|
@ -37,8 +39,8 @@ class OnionrCrypto:
|
|||
|
||||
# Load our own pub/priv Ed25519 keys, gen & save them if they don't exist
|
||||
if os.path.exists(self._keyFile):
|
||||
if len(config.get('general.public_key', '')) > 0:
|
||||
self.pubKey = config.get('general.public_key')
|
||||
if len(self._core.config.get('general.public_key', '')) > 0:
|
||||
self.pubKey = self._core.config.get('general.public_key')
|
||||
else:
|
||||
self.pubKey = self.keyManager.getPubkeyList()[0]
|
||||
self.privKey = self.keyManager.getPrivkey(self.pubKey)
|
||||
|
@ -93,9 +95,10 @@ class OnionrCrypto:
|
|||
|
||||
def pubKeyEncrypt(self, data, pubkey, encodedData=False):
|
||||
'''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)'''
|
||||
pubkey = unpaddedbase32.repad(bytesconverter.str_to_bytes(pubkey))
|
||||
retVal = ''
|
||||
box = None
|
||||
data = self._core._utils.strToBytes(data)
|
||||
data = bytesconverter.str_to_bytes(data)
|
||||
|
||||
pubkey = nacl.signing.VerifyKey(pubkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_public_key()
|
||||
|
||||
|
@ -120,7 +123,7 @@ class OnionrCrypto:
|
|||
privkey = self.privKey
|
||||
ownKey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
|
||||
|
||||
if self._core._utils.validatePubKey(privkey):
|
||||
if stringvalidators.validate_pub_key(privkey):
|
||||
privkey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
|
||||
anonBox = nacl.public.SealedBox(privkey)
|
||||
else:
|
||||
|
@ -129,7 +132,7 @@ class OnionrCrypto:
|
|||
return decrypted
|
||||
|
||||
def symmetricEncrypt(self, data, key, encodedKey=False, returnEncoded=True):
|
||||
'''Encrypt data to a 32-byte key (Salsa20-Poly1305 MAC)'''
|
||||
'''Encrypt data with a 32-byte key (Salsa20-Poly1305 MAC)'''
|
||||
if encodedKey:
|
||||
encoding = nacl.encoding.Base64Encoder
|
||||
else:
|
||||
|
@ -179,7 +182,7 @@ class OnionrCrypto:
|
|||
def generateDeterministic(self, passphrase, bypassCheck=False):
|
||||
'''Generate a Ed25519 public key pair from a password'''
|
||||
passStrength = self.deterministicRequirement
|
||||
passphrase = self._core._utils.strToBytes(passphrase) # Convert to bytes if not already
|
||||
passphrase = bytesconverter.str_to_bytes(passphrase) # Convert to bytes if not already
|
||||
# Validate passphrase length
|
||||
if not bypassCheck:
|
||||
if len(passphrase) < passStrength:
|
||||
|
@ -199,7 +202,7 @@ class OnionrCrypto:
|
|||
if pubkey == '':
|
||||
pubkey = self.pubKey
|
||||
prev = ''
|
||||
pubkey = pubkey.encode()
|
||||
pubkey = bytesconverter.str_to_bytes(pubkey)
|
||||
for i in range(self.HASH_ID_ROUNDS):
|
||||
try:
|
||||
prev = prev.encode()
|
||||
|
@ -248,8 +251,8 @@ class OnionrCrypto:
|
|||
|
||||
difficulty = onionrproofs.getDifficultyForNewBlock(blockContent, ourBlock=False, coreInst=self._core)
|
||||
|
||||
if difficulty < int(config.get('general.minimum_block_pow')):
|
||||
difficulty = int(config.get('general.minimum_block_pow'))
|
||||
if difficulty < int(self._core.config.get('general.minimum_block_pow')):
|
||||
difficulty = int(self._core.config.get('general.minimum_block_pow'))
|
||||
mainHash = '0000000000000000000000000000000000000000000000000000000000000000'#nacl.hash.blake2b(nacl.utils.random()).decode()
|
||||
puzzle = mainHash[:difficulty]
|
||||
|
||||
|
@ -263,7 +266,7 @@ class OnionrCrypto:
|
|||
|
||||
@staticmethod
|
||||
def replayTimestampValidation(timestamp):
|
||||
if core.Core()._utils.getEpoch() - int(timestamp) > 2419200:
|
||||
if epoch.get_epoch() - int(timestamp) > 2419200:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
'''
|
||||
import sqlite3
|
||||
import core, config, logger
|
||||
from onionrutils import epoch
|
||||
config.reload()
|
||||
class PeerProfiles:
|
||||
'''
|
||||
|
@ -106,7 +107,7 @@ def peerCleanup(coreInst):
|
|||
if PeerProfiles(address, coreInst).score < minScore:
|
||||
coreInst.removeAddress(address)
|
||||
try:
|
||||
if (int(coreInst._utils.getEpoch()) - int(coreInst.getPeerInfo(address, 'dateSeen'))) >= 600:
|
||||
if (int(epoch.get_epoch()) - int(coreInst.getPeerInfo(address, 'dateSeen'))) >= 600:
|
||||
expireTime = 600
|
||||
else:
|
||||
expireTime = 86400
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
'''
|
||||
|
||||
import onionrplugins, core as onionrcore, logger
|
||||
from onionrutils import localcommand
|
||||
|
||||
class DaemonAPI:
|
||||
def __init__(self, pluginapi):
|
||||
|
@ -40,7 +41,7 @@ class DaemonAPI:
|
|||
return
|
||||
|
||||
def local_command(self, command):
|
||||
return self.pluginapi.get_utils().localCommand(self, command)
|
||||
return localcommand.local_command(self.pluginapi.get_core(), command)
|
||||
|
||||
def queue_pop(self):
|
||||
return self.get_core().daemonQueue()
|
||||
|
@ -169,9 +170,6 @@ class pluginapi:
|
|||
def get_core(self):
|
||||
return self.core
|
||||
|
||||
def get_utils(self):
|
||||
return self.get_core()._utils
|
||||
|
||||
def get_crypto(self):
|
||||
return self.get_core()._crypto
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import multiprocessing, nacl.encoding, nacl.hash, nacl.utils, time, math, threading, binascii, sys, json
|
||||
import core, onionrutils, config, logger, onionrblockapi
|
||||
import core, config, logger, onionrblockapi
|
||||
from onionrutils import bytesconverter
|
||||
|
||||
config.reload()
|
||||
|
||||
|
@ -29,12 +30,7 @@ def getDifficultyModifier(coreOrUtilsInst=None):
|
|||
'''
|
||||
classInst = coreOrUtilsInst
|
||||
retData = 0
|
||||
if isinstance(classInst, core.Core):
|
||||
useFunc = classInst._utils.storageCounter.getPercent
|
||||
elif isinstance(classInst, onionrutils.OnionrUtils):
|
||||
useFunc = classInst.storageCounter.getPercent
|
||||
else:
|
||||
useFunc = core.Core()._utils.storageCounter.getPercent
|
||||
useFunc = classInst.storage_counter.getPercent
|
||||
|
||||
percentUse = useFunc()
|
||||
|
||||
|
@ -56,7 +52,7 @@ def getDifficultyForNewBlock(data, ourBlock=True, coreInst=None):
|
|||
if isinstance(data, onionrblockapi.Block):
|
||||
dataSize = len(data.getRaw().encode('utf-8'))
|
||||
else:
|
||||
dataSize = len(onionrutils.OnionrUtils.strToBytes(data))
|
||||
dataSize = len(bytesconverter.str_to_bytes(data))
|
||||
|
||||
if ourBlock:
|
||||
minDifficulty = config.get('general.minimum_send_pow', 4)
|
||||
|
|
|
@ -21,6 +21,7 @@ import time
|
|||
import stem
|
||||
import core
|
||||
from . import connectionserver, bootstrapservice
|
||||
from onionrutils import stringvalidators, basicrequests
|
||||
|
||||
class OnionrServices:
|
||||
'''
|
||||
|
@ -39,14 +40,14 @@ class OnionrServices:
|
|||
When a client wants to connect, contact their bootstrap address and tell them our
|
||||
ephemeral address for our service by creating a new ConnectionServer instance
|
||||
'''
|
||||
assert self._core._utils.validateID(address)
|
||||
assert stringvalidators.validate_transport(address)
|
||||
BOOTSTRAP_TRIES = 10 # How many times to attempt contacting the bootstrap server
|
||||
TRY_WAIT = 3 # Seconds to wait before trying bootstrap again
|
||||
# HTTP is fine because .onion/i2p is encrypted/authenticated
|
||||
base_url = 'http://%s/' % (address,)
|
||||
socks = self._core.config.get('tor.socksport')
|
||||
for x in range(BOOTSTRAP_TRIES):
|
||||
if self._core._utils.doGetRequest(base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!':
|
||||
if basicrequests.do_get_request(self._core, base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!':
|
||||
# if bootstrap sever is online, tell them our service address
|
||||
connectionserver.ConnectionServer(peer, address, core_inst=self._core)
|
||||
else:
|
||||
|
|
|
@ -24,6 +24,7 @@ from flask import Flask, Response
|
|||
import core
|
||||
from netcontroller import getOpenPort
|
||||
from . import httpheaders
|
||||
from onionrutils import stringvalidators, epoch
|
||||
|
||||
def bootstrap_client_service(peer, core_inst=None, bootstrap_timeout=300):
|
||||
'''
|
||||
|
@ -32,7 +33,7 @@ def bootstrap_client_service(peer, core_inst=None, bootstrap_timeout=300):
|
|||
if core_inst is None:
|
||||
core_inst = core.Core()
|
||||
|
||||
if not core_inst._utils.validatePubKey(peer):
|
||||
if not stringvalidators.validate_pub_key(peer):
|
||||
raise ValueError('Peer must be valid base32 ed25519 public key')
|
||||
|
||||
bootstrap_port = getOpenPort()
|
||||
|
@ -61,7 +62,7 @@ def bootstrap_client_service(peer, core_inst=None, bootstrap_timeout=300):
|
|||
|
||||
@bootstrap_app.route('/bs/<address>', methods=['POST'])
|
||||
def get_bootstrap(address):
|
||||
if core_inst._utils.validateID(address + '.onion'):
|
||||
if stringvalidators.validate_transport(address + '.onion'):
|
||||
# Set the bootstrap address then close the server
|
||||
bootstrap_address = address + '.onion'
|
||||
core_inst.keyStore.put(bs_id, bootstrap_address)
|
||||
|
@ -76,7 +77,7 @@ def bootstrap_client_service(peer, core_inst=None, bootstrap_timeout=300):
|
|||
# Create the v3 onion service
|
||||
response = controller.create_ephemeral_hidden_service({80: bootstrap_port}, key_type = 'NEW', key_content = 'ED25519-V3', await_publication = True)
|
||||
core_inst.insertBlock(response.service_id, header='con', sign=True, encryptType='asym',
|
||||
asymPeer=peer, disableForward=True, expire=(core_inst._utils.getEpoch() + bootstrap_timeout))
|
||||
asymPeer=peer, disableForward=True, expire=(epoch.get_epoch() + bootstrap_timeout))
|
||||
# Run the bootstrap server
|
||||
try:
|
||||
http_server.serve_forever()
|
||||
|
|
|
@ -24,6 +24,7 @@ import core, logger, httpapi
|
|||
import onionrexceptions
|
||||
from netcontroller import getOpenPort
|
||||
import api
|
||||
from onionrutils import stringvalidators, basicrequests
|
||||
from . import httpheaders
|
||||
|
||||
class ConnectionServer:
|
||||
|
@ -33,7 +34,7 @@ class ConnectionServer:
|
|||
else:
|
||||
self.core_inst = core_inst
|
||||
|
||||
if not core_inst._utils.validatePubKey(peer):
|
||||
if not stringvalidators.validate_pub_key(peer):
|
||||
raise ValueError('Peer must be valid base32 ed25519 public key')
|
||||
|
||||
socks = core_inst.config.get('tor.socksport') # Load config for Tor socks port for proxy
|
||||
|
@ -71,7 +72,7 @@ class ConnectionServer:
|
|||
|
||||
try:
|
||||
for x in range(3):
|
||||
attempt = self.core_inst._utils.doPostRequest('http://' + address + '/bs/' + response.service_id, port=socks)
|
||||
attempt = basicrequests.do_post_request(self.core_inst, 'http://' + address + '/bs/' + response.service_id, port=socks)
|
||||
if attempt == 'success':
|
||||
break
|
||||
else:
|
||||
|
|
|
@ -17,7 +17,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 core, sys, sqlite3, os, dbcreator
|
||||
import core, sys, sqlite3, os, dbcreator, onionrexceptions
|
||||
from onionrutils import bytesconverter, stringvalidators
|
||||
|
||||
DB_ENTRY_SIZE_LIMIT = 10000 # Will be a config option
|
||||
|
||||
|
@ -65,7 +66,7 @@ def deleteBlock(coreInst, blockHash):
|
|||
|
||||
def store(coreInst, data, blockHash=''):
|
||||
assert isinstance(coreInst, core.Core)
|
||||
assert coreInst._utils.validateHash(blockHash)
|
||||
assert stringvalidators.validate_hash(blockHash)
|
||||
ourHash = coreInst._crypto.sha3Hash(data)
|
||||
if blockHash != '':
|
||||
assert ourHash == blockHash
|
||||
|
@ -80,9 +81,9 @@ def store(coreInst, data, blockHash=''):
|
|||
|
||||
def getData(coreInst, bHash):
|
||||
assert isinstance(coreInst, core.Core)
|
||||
assert coreInst._utils.validateHash(bHash)
|
||||
assert stringvalidators.validate_hash(bHash)
|
||||
|
||||
bHash = coreInst._utils.bytesToStr(bHash)
|
||||
bHash = bytesconverter.bytes_to_str(bHash)
|
||||
|
||||
# First check DB for data entry by hash
|
||||
# if no entry, check disk
|
||||
|
@ -94,4 +95,6 @@ def getData(coreInst, bHash):
|
|||
retData = block.read()
|
||||
else:
|
||||
retData = _dbFetch(coreInst, bHash)
|
||||
if retData is None:
|
||||
raise onionrexceptions.NoDataAvailable("Block data for %s is not available" % [bHash])
|
||||
return retData
|
21
onionr/onionrstorage/removeblock.py
Normal file
21
onionr/onionrstorage/removeblock.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import sys, sqlite3
|
||||
import onionrexceptions, onionrstorage
|
||||
from onionrutils import stringvalidators
|
||||
def remove_block(core_inst, block):
|
||||
'''
|
||||
remove a block from this node (does not automatically blacklist)
|
||||
|
||||
**You may want blacklist.addToDB(blockHash)
|
||||
'''
|
||||
|
||||
if stringvalidators.validate_hash(block):
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
t = (block,)
|
||||
c.execute('Delete from hashes where hash=?;', t)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
dataSize = sys.getsizeof(onionrstorage.getData(core_inst, block))
|
||||
core_inst.storage_counter.removeBytes(dataSize)
|
||||
else:
|
||||
raise onionrexceptions.InvalidHexHash
|
36
onionr/onionrstorage/setdata.py
Normal file
36
onionr/onionrstorage/setdata.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import sys, sqlite3
|
||||
import onionrstorage, onionrexceptions
|
||||
def set_data(core_inst, data):
|
||||
'''
|
||||
Set the data assciated with a hash
|
||||
'''
|
||||
|
||||
data = data
|
||||
dataSize = sys.getsizeof(data)
|
||||
|
||||
if not type(data) is bytes:
|
||||
data = data.encode()
|
||||
|
||||
dataHash = core_inst._crypto.sha3Hash(data)
|
||||
|
||||
if type(dataHash) is bytes:
|
||||
dataHash = dataHash.decode()
|
||||
blockFileName = core_inst.blockDataLocation + dataHash + '.dat'
|
||||
try:
|
||||
onionrstorage.getData(core_inst, dataHash)
|
||||
except onionrexceptions.NoDataAvailable:
|
||||
if core_inst.storage_counter.addBytes(dataSize) != False:
|
||||
onionrstorage.store(core_inst, data, blockHash=dataHash)
|
||||
conn = sqlite3.connect(core_inst.blockDB, timeout=30)
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE hashes SET dataSaved=1 WHERE hash = ?;", (dataHash,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
with open(core_inst.dataNonceFile, 'a') as nonceFile:
|
||||
nonceFile.write(dataHash + '\n')
|
||||
else:
|
||||
raise onionrexceptions.DiskAllocationReached
|
||||
else:
|
||||
raise Exception("Data is already set for " + dataHash)
|
||||
|
||||
return dataHash
|
|
@ -18,10 +18,13 @@
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import os, json, onionrexceptions
|
||||
import unpaddedbase32
|
||||
from onionrusers import onionrusers
|
||||
from onionrutils import bytesconverter, epoch
|
||||
|
||||
class ContactManager(onionrusers.OnionrUser):
|
||||
def __init__(self, coreInst, publicKey, saveUser=False, recordExpireSeconds=5):
|
||||
publicKey = unpaddedbase32.repad(bytesconverter.str_to_bytes(publicKey)).decode()
|
||||
super(ContactManager, self).__init__(coreInst, publicKey, saveUser=saveUser)
|
||||
self.dataDir = coreInst.dataDir + '/contacts/'
|
||||
self.dataFile = '%s/contacts/%s.json' % (coreInst.dataDir, publicKey)
|
||||
|
@ -39,7 +42,7 @@ class ContactManager(onionrusers.OnionrUser):
|
|||
dataFile.write(data)
|
||||
|
||||
def _loadData(self):
|
||||
self.lastRead = self._core._utils.getEpoch()
|
||||
self.lastRead = epoch.get_epoch()
|
||||
retData = {}
|
||||
if os.path.exists(self.dataFile):
|
||||
with open(self.dataFile, 'r') as dataFile:
|
||||
|
@ -59,7 +62,7 @@ class ContactManager(onionrusers.OnionrUser):
|
|||
if self.deleted:
|
||||
raise onionrexceptions.ContactDeleted
|
||||
|
||||
if (self._core._utils.getEpoch() - self.lastRead >= self.recordExpire) or forceReload:
|
||||
if (epoch.get_epoch() - self.lastRead >= self.recordExpire) or forceReload:
|
||||
self.data = self._loadData()
|
||||
try:
|
||||
return self.data[key]
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
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 onionrblockapi, logger, onionrexceptions, json, sqlite3, time
|
||||
import logger, onionrexceptions, json, sqlite3, time
|
||||
from onionrutils import stringvalidators, bytesconverter, epoch
|
||||
import unpaddedbase32
|
||||
import nacl.exceptions
|
||||
|
||||
def deleteExpiredKeys(coreInst):
|
||||
|
@ -25,7 +27,7 @@ def deleteExpiredKeys(coreInst):
|
|||
conn = sqlite3.connect(coreInst.forwardKeysFile, timeout=10)
|
||||
c = conn.cursor()
|
||||
|
||||
curTime = coreInst._utils.getEpoch()
|
||||
curTime = epoch.get_epoch()
|
||||
c.execute("DELETE from myForwardKeys where expire <= ?", (curTime,))
|
||||
conn.commit()
|
||||
conn.execute("VACUUM")
|
||||
|
@ -37,7 +39,7 @@ def deleteTheirExpiredKeys(coreInst, pubkey):
|
|||
c = conn.cursor()
|
||||
|
||||
# Prepare the insert
|
||||
command = (pubkey, coreInst._utils.getEpoch())
|
||||
command = (pubkey, epoch.get_epoch())
|
||||
|
||||
c.execute("DELETE from forwardKeys where peerKey = ? and expire <= ?", command)
|
||||
|
||||
|
@ -55,8 +57,7 @@ class OnionrUser:
|
|||
Takes an instance of onionr core, a base32 encoded ed25519 public key, and a bool saveUser
|
||||
saveUser determines if we should add a user to our peer database or not.
|
||||
'''
|
||||
if ' ' in coreInst._utils.bytesToStr(publicKey).strip():
|
||||
publicKey = coreInst._utils.convertHumanReadableID(publicKey)
|
||||
publicKey = unpaddedbase32.repad(bytesconverter.str_to_bytes(publicKey)).decode()
|
||||
|
||||
self.trust = 0
|
||||
self._core = coreInst
|
||||
|
@ -103,7 +104,7 @@ class OnionrUser:
|
|||
deleteExpiredKeys(self._core)
|
||||
retData = ''
|
||||
forwardKey = self._getLatestForwardKey()
|
||||
if self._core._utils.validatePubKey(forwardKey[0]):
|
||||
if stringvalidators.validate_pub_key(forwardKey[0]):
|
||||
retData = self._core._crypto.pubKeyEncrypt(data, forwardKey[0], encodedData=True)
|
||||
else:
|
||||
raise onionrexceptions.InvalidPubkey("No valid forward secrecy key available for this user")
|
||||
|
@ -158,10 +159,10 @@ class OnionrUser:
|
|||
conn = sqlite3.connect(self._core.forwardKeysFile, timeout=10)
|
||||
c = conn.cursor()
|
||||
# Prepare the insert
|
||||
time = self._core._utils.getEpoch()
|
||||
time = epoch.get_epoch()
|
||||
newKeys = self._core._crypto.generatePubKey()
|
||||
newPub = self._core._utils.bytesToStr(newKeys[0])
|
||||
newPriv = self._core._utils.bytesToStr(newKeys[1])
|
||||
newPub = bytesconverter.bytes_to_str(newKeys[0])
|
||||
newPriv = bytesconverter.bytes_to_str(newKeys[1])
|
||||
|
||||
command = (self.publicKey, newPub, newPriv, time, expire + time)
|
||||
|
||||
|
@ -176,7 +177,7 @@ class OnionrUser:
|
|||
conn = sqlite3.connect(self._core.forwardKeysFile, timeout=10)
|
||||
c = conn.cursor()
|
||||
pubkey = self.publicKey
|
||||
pubkey = self._core._utils.bytesToStr(pubkey)
|
||||
pubkey = bytesconverter.bytes_to_str(pubkey)
|
||||
command = (pubkey,)
|
||||
keyList = [] # list of tuples containing pub, private for peer
|
||||
|
||||
|
@ -190,7 +191,8 @@ class OnionrUser:
|
|||
return list(keyList)
|
||||
|
||||
def addForwardKey(self, newKey, expire=DEFAULT_KEY_EXPIRE):
|
||||
if not self._core._utils.validatePubKey(newKey):
|
||||
newKey = bytesconverter.bytes_to_str(unpaddedbase32.repad(bytesconverter.str_to_bytes(newKey)))
|
||||
if not stringvalidators.validate_pub_key(newKey):
|
||||
# Do not add if something went wrong with the key
|
||||
raise onionrexceptions.InvalidPubkey(newKey)
|
||||
|
||||
|
@ -198,7 +200,7 @@ class OnionrUser:
|
|||
c = conn.cursor()
|
||||
|
||||
# Get the time we're inserting the key at
|
||||
timeInsert = self._core._utils.getEpoch()
|
||||
timeInsert = epoch.get_epoch()
|
||||
|
||||
# Look at our current keys for duplicate key data or time
|
||||
for entry in self._getForwardKeys():
|
||||
|
|
|
@ -1,572 +0,0 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
OnionrUtils offers various useful functions to Onionr. Relatively misc.
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
# Misc functions that do not fit in the main api, but are useful
|
||||
import sys, os, sqlite3, binascii, time, base64, json, glob, shutil, math, re, urllib.parse, string
|
||||
import requests
|
||||
import nacl.signing, nacl.encoding
|
||||
from onionrblockapi import Block
|
||||
import onionrexceptions, config, logger
|
||||
from onionr import API_VERSION
|
||||
import onionrevents
|
||||
import storagecounter
|
||||
from etc import pgpwords, onionrvalues
|
||||
from onionrusers import onionrusers
|
||||
if sys.version_info < (3, 6):
|
||||
try:
|
||||
import sha3
|
||||
except ModuleNotFoundError:
|
||||
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
|
||||
sys.exit(1)
|
||||
config.reload()
|
||||
class OnionrUtils:
|
||||
'''
|
||||
Various useful functions for validating things, etc functions, connectivity
|
||||
'''
|
||||
def __init__(self, coreInstance):
|
||||
#self.fingerprintFile = 'data/own-fingerprint.txt' #TODO Remove since probably not needed
|
||||
self._core = coreInstance # onionr core instance
|
||||
|
||||
self.timingToken = '' # for when we make local connections to our http api, to bypass timing attack defense mechanism
|
||||
self.avoidDupe = [] # list used to prevent duplicate requests per peer for certain actions
|
||||
self.peerProcessing = {} # dict of current peer actions: peer, actionList
|
||||
self.storageCounter = storagecounter.StorageCounter(self._core) # used to keep track of how much data onionr is using on disk
|
||||
return
|
||||
|
||||
def getTimeBypassToken(self):
|
||||
'''
|
||||
Load our timingToken from disk for faster local HTTP API
|
||||
'''
|
||||
try:
|
||||
if os.path.exists(self._core.dataDir + 'time-bypass.txt'):
|
||||
with open(self._core.dataDir + 'time-bypass.txt', 'r') as bypass:
|
||||
self.timingToken = bypass.read()
|
||||
except Exception as error:
|
||||
logger.error('Failed to fetch time bypass token.', error = error)
|
||||
|
||||
return self.timingToken
|
||||
|
||||
def getRoundedEpoch(self, roundS=60):
|
||||
'''
|
||||
Returns the epoch, rounded down to given seconds (Default 60)
|
||||
'''
|
||||
epoch = self.getEpoch()
|
||||
return epoch - (epoch % roundS)
|
||||
|
||||
def getClientAPIServer(self):
|
||||
retData = ''
|
||||
try:
|
||||
with open(self._core.privateApiHostFile, 'r') as host:
|
||||
hostname = host.read()
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError
|
||||
else:
|
||||
retData += '%s:%s' % (hostname, config.get('client.client.port'))
|
||||
return retData
|
||||
|
||||
def localCommand(self, command, data='', silent = True, post=False, postData = {}, maxWait=20):
|
||||
'''
|
||||
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
|
||||
'''
|
||||
self.getTimeBypassToken()
|
||||
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
|
||||
hostname = ''
|
||||
waited = 0
|
||||
while hostname == '':
|
||||
try:
|
||||
hostname = self.getClientAPIServer()
|
||||
except FileNotFoundError:
|
||||
time.sleep(1)
|
||||
waited += 1
|
||||
if waited == maxWait:
|
||||
return False
|
||||
if data != '':
|
||||
data = '&data=' + urllib.parse.quote_plus(data)
|
||||
payload = 'http://%s/%s%s' % (hostname, command, data)
|
||||
try:
|
||||
if post:
|
||||
retData = requests.post(payload, data=postData, headers={'token': config.get('client.webpassword'), 'Connection':'close'}, timeout=(maxWait, maxWait)).text
|
||||
else:
|
||||
retData = requests.get(payload, headers={'token': config.get('client.webpassword'), 'Connection':'close'}, timeout=(maxWait, maxWait)).text
|
||||
except Exception as error:
|
||||
if not silent:
|
||||
logger.error('Failed to make local request (command: %s):%s' % (command, error))
|
||||
retData = False
|
||||
|
||||
return retData
|
||||
|
||||
def getHumanReadableID(self, pub=''):
|
||||
'''gets a human readable ID from a public key'''
|
||||
if pub == '':
|
||||
pub = self._core._crypto.pubKey
|
||||
pub = base64.b16encode(base64.b32decode(pub)).decode()
|
||||
return ' '.join(pgpwords.wordify(pub))
|
||||
|
||||
def convertHumanReadableID(self, pub):
|
||||
'''Convert a human readable pubkey id to base32'''
|
||||
pub = pub.lower()
|
||||
return self.bytesToStr(base64.b32encode(binascii.unhexlify(pgpwords.hexify(pub.strip()))))
|
||||
|
||||
def getBlockMetadataFromData(self, blockData):
|
||||
'''
|
||||
accepts block contents as string, returns a tuple of
|
||||
metadata, meta (meta being internal metadata, which will be
|
||||
returned as an encrypted base64 string if it is encrypted, dict if not).
|
||||
'''
|
||||
meta = {}
|
||||
metadata = {}
|
||||
data = blockData
|
||||
try:
|
||||
blockData = blockData.encode()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
metadata = json.loads(blockData[:blockData.find(b'\n')].decode())
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
else:
|
||||
data = blockData[blockData.find(b'\n'):].decode()
|
||||
|
||||
if not metadata['encryptType'] in ('asym', 'sym'):
|
||||
try:
|
||||
meta = json.loads(metadata['meta'])
|
||||
except KeyError:
|
||||
pass
|
||||
meta = metadata['meta']
|
||||
return (metadata, meta, data)
|
||||
|
||||
def processBlockMetadata(self, blockHash):
|
||||
'''
|
||||
Read metadata from a block and cache it to the block database
|
||||
'''
|
||||
curTime = self.getRoundedEpoch(roundS=60)
|
||||
myBlock = Block(blockHash, self._core)
|
||||
if myBlock.isEncrypted:
|
||||
myBlock.decrypt()
|
||||
if (myBlock.isEncrypted and myBlock.decrypted) or (not myBlock.isEncrypted):
|
||||
blockType = myBlock.getMetadata('type') # we would use myBlock.getType() here, but it is bugged with encrypted blocks
|
||||
signer = self.bytesToStr(myBlock.signer)
|
||||
valid = myBlock.verifySig()
|
||||
if myBlock.getMetadata('newFSKey') is not None:
|
||||
onionrusers.OnionrUser(self._core, signer).addForwardKey(myBlock.getMetadata('newFSKey'))
|
||||
|
||||
try:
|
||||
if len(blockType) <= 10:
|
||||
self._core.updateBlockInfo(blockHash, 'dataType', blockType)
|
||||
except TypeError:
|
||||
logger.warn("Missing block information")
|
||||
pass
|
||||
# Set block expire time if specified
|
||||
try:
|
||||
expireTime = myBlock.getHeader('expire')
|
||||
assert len(str(int(expireTime))) < 20 # test that expire time is an integer of sane length (for epoch)
|
||||
except (AssertionError, ValueError, TypeError) as e:
|
||||
expireTime = onionrvalues.OnionrValues().default_expire + curTime
|
||||
finally:
|
||||
self._core.updateBlockInfo(blockHash, 'expire', expireTime)
|
||||
if not blockType is None:
|
||||
self._core.updateBlockInfo(blockHash, 'dataType', blockType)
|
||||
onionrevents.event('processblocks', data = {'block': myBlock, 'type': blockType, 'signer': signer, 'validSig': valid}, onionr = self._core.onionrInst)
|
||||
else:
|
||||
pass
|
||||
#logger.debug('Not processing metadata on encrypted block we cannot decrypt.')
|
||||
|
||||
def escapeAnsi(self, line):
|
||||
'''
|
||||
Remove ANSI escape codes from a string with regex
|
||||
|
||||
taken or adapted from: https://stackoverflow.com/a/38662876 by user https://stackoverflow.com/users/802365/%c3%89douard-lopez
|
||||
cc-by-sa-3 license https://creativecommons.org/licenses/by-sa/3.0/
|
||||
'''
|
||||
ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
|
||||
return ansi_escape.sub('', line)
|
||||
|
||||
def hasBlock(self, hash):
|
||||
'''
|
||||
Check for new block in the list
|
||||
'''
|
||||
conn = sqlite3.connect(self._core.blockDB)
|
||||
c = conn.cursor()
|
||||
if not self.validateHash(hash):
|
||||
raise Exception("Invalid hash")
|
||||
for result in c.execute("SELECT COUNT() FROM hashes WHERE hash = ?", (hash,)):
|
||||
if result[0] >= 1:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
else:
|
||||
conn.commit()
|
||||
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 hash hex digest (does not compare, just checks length and charset)
|
||||
'''
|
||||
retVal = True
|
||||
if data == False or data == True:
|
||||
return False
|
||||
data = data.strip()
|
||||
if len(data) != length:
|
||||
retVal = False
|
||||
else:
|
||||
try:
|
||||
int(data, 16)
|
||||
except ValueError:
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
|
||||
def validateMetadata(self, metadata, blockData):
|
||||
'''Validate metadata meets onionr spec (does not validate proof value computation), take in either dictionary or json string'''
|
||||
# TODO, make this check sane sizes
|
||||
retData = False
|
||||
maxClockDifference = 120
|
||||
|
||||
# convert to dict if it is json string
|
||||
if type(metadata) is str:
|
||||
try:
|
||||
metadata = json.loads(metadata)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# Validate metadata dict for invalid keys to sizes that are too large
|
||||
maxAge = config.get("general.max_block_age", onionrvalues.OnionrValues().default_expire)
|
||||
if type(metadata) is dict:
|
||||
for i in metadata:
|
||||
try:
|
||||
self._core.requirements.blockMetadataLengths[i]
|
||||
except KeyError:
|
||||
logger.warn('Block has invalid metadata key ' + i)
|
||||
break
|
||||
else:
|
||||
testData = metadata[i]
|
||||
try:
|
||||
testData = len(testData)
|
||||
except (TypeError, AttributeError) as e:
|
||||
testData = len(str(testData))
|
||||
if self._core.requirements.blockMetadataLengths[i] < testData:
|
||||
logger.warn('Block metadata key ' + i + ' exceeded maximum size')
|
||||
break
|
||||
if i == 'time':
|
||||
if not self.isIntegerString(metadata[i]):
|
||||
logger.warn('Block metadata time stamp is not integer string or int')
|
||||
break
|
||||
isFuture = (metadata[i] - self.getEpoch())
|
||||
if isFuture > maxClockDifference:
|
||||
logger.warn('Block timestamp is skewed to the future over the max %s: %s' (maxClockDifference, isFuture))
|
||||
break
|
||||
if (self.getEpoch() - metadata[i]) > maxAge:
|
||||
logger.warn('Block is outdated: %s' % (metadata[i],))
|
||||
break
|
||||
elif i == 'expire':
|
||||
try:
|
||||
assert int(metadata[i]) > self.getEpoch()
|
||||
except AssertionError:
|
||||
logger.warn('Block is expired: %s less than %s' % (metadata[i], self.getEpoch()))
|
||||
break
|
||||
elif i == 'encryptType':
|
||||
try:
|
||||
assert metadata[i] in ('asym', 'sym', '')
|
||||
except AssertionError:
|
||||
logger.warn('Invalid encryption mode')
|
||||
break
|
||||
else:
|
||||
# if metadata loop gets no errors, it does not break, therefore metadata is valid
|
||||
# make sure we do not have another block with the same data content (prevent data duplication and replay attacks)
|
||||
nonce = self._core._utils.bytesToStr(self._core._crypto.sha3Hash(blockData))
|
||||
try:
|
||||
with open(self._core.dataNonceFile, 'r') as nonceFile:
|
||||
if nonce in nonceFile.read():
|
||||
retData = False # we've seen that nonce before, so we can't pass metadata
|
||||
raise onionrexceptions.DataExists
|
||||
except FileNotFoundError:
|
||||
retData = True
|
||||
except onionrexceptions.DataExists:
|
||||
# do not set retData to True, because nonce has been seen before
|
||||
pass
|
||||
else:
|
||||
retData = True
|
||||
else:
|
||||
logger.warn('In call to utils.validateMetadata, metadata must be JSON string or a dictionary object')
|
||||
|
||||
return retData
|
||||
|
||||
def validatePubKey(self, key):
|
||||
'''
|
||||
Validate if a string is a valid base32 encoded Ed25519 key
|
||||
'''
|
||||
retVal = False
|
||||
if type(key) is type(None):
|
||||
return False
|
||||
try:
|
||||
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
|
||||
except nacl.exceptions.ValueError:
|
||||
pass
|
||||
except base64.binascii.Error as err:
|
||||
pass
|
||||
else:
|
||||
retVal = True
|
||||
return retVal
|
||||
|
||||
@staticmethod
|
||||
def validateID(id):
|
||||
'''
|
||||
Validate if an address is a valid tor or i2p hidden service
|
||||
'''
|
||||
try:
|
||||
idLength = len(id)
|
||||
retVal = True
|
||||
idNoDomain = ''
|
||||
peerType = ''
|
||||
# i2p b32 addresses are 60 characters long (including .b32.i2p)
|
||||
if idLength == 60:
|
||||
peerType = 'i2p'
|
||||
if not id.endswith('.b32.i2p'):
|
||||
retVal = False
|
||||
else:
|
||||
idNoDomain = id.split('.b32.i2p')[0]
|
||||
# Onion v2's are 22 (including .onion), v3's are 62 with .onion
|
||||
elif idLength == 22 or idLength == 62:
|
||||
peerType = 'onion'
|
||||
if not id.endswith('.onion'):
|
||||
retVal = False
|
||||
else:
|
||||
idNoDomain = id.split('.onion')[0]
|
||||
else:
|
||||
retVal = False
|
||||
if retVal:
|
||||
if peerType == 'i2p':
|
||||
try:
|
||||
id.split('.b32.i2p')[2]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
retVal = False
|
||||
elif peerType == 'onion':
|
||||
try:
|
||||
id.split('.onion')[2]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
retVal = False
|
||||
if not idNoDomain.isalnum():
|
||||
retVal = False
|
||||
|
||||
# Validate address is valid base32 (when capitalized and minus extension); v2/v3 onions and .b32.i2p use base32
|
||||
for x in idNoDomain.upper():
|
||||
if x not in string.ascii_uppercase and x not in '234567':
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
except:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def isIntegerString(data):
|
||||
'''Check if a string is a valid base10 integer (also returns true if already an int)'''
|
||||
try:
|
||||
int(data)
|
||||
except (ValueError, TypeError) as e:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def isCommunicatorRunning(self, timeout = 5, interval = 0.1):
|
||||
try:
|
||||
runcheck_file = self._core.dataDir + '.runcheck'
|
||||
|
||||
if not os.path.isfile(runcheck_file):
|
||||
open(runcheck_file, 'w+').close()
|
||||
|
||||
# self._core.daemonQueueAdd('runCheck') # deprecated
|
||||
starttime = time.time()
|
||||
|
||||
while True:
|
||||
time.sleep(interval)
|
||||
|
||||
if not os.path.isfile(runcheck_file):
|
||||
return True
|
||||
elif time.time() - starttime >= timeout:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def importNewBlocks(self, scanDir=''):
|
||||
'''
|
||||
This function is intended to scan for new blocks ON THE DISK and import them
|
||||
'''
|
||||
blockList = self._core.getBlockList()
|
||||
exist = False
|
||||
if scanDir == '':
|
||||
scanDir = self._core.blockDataLocation
|
||||
if not scanDir.endswith('/'):
|
||||
scanDir += '/'
|
||||
for block in glob.glob(scanDir + "*.dat"):
|
||||
if block.replace(scanDir, '').replace('.dat', '') not in blockList:
|
||||
exist = True
|
||||
logger.info('Found new block on dist %s' % block)
|
||||
with open(block, 'rb') as newBlock:
|
||||
block = block.replace(scanDir, '').replace('.dat', '')
|
||||
if self._core._crypto.sha3Hash(newBlock.read()) == block.replace('.dat', ''):
|
||||
self._core.addToBlockDB(block.replace('.dat', ''), dataSaved=True)
|
||||
logger.info('Imported block %s.' % block)
|
||||
self._core._utils.processBlockMetadata(block)
|
||||
else:
|
||||
logger.warn('Failed to verify hash for %s' % block)
|
||||
if not exist:
|
||||
logger.info('No blocks found to import')
|
||||
|
||||
def progressBar(self, value = 0, endvalue = 100, width = None):
|
||||
'''
|
||||
Outputs a progress bar with a percentage. Write \n after use.
|
||||
'''
|
||||
|
||||
if width is None or height is None:
|
||||
width, height = shutil.get_terminal_size((80, 24))
|
||||
|
||||
bar_length = width - 6
|
||||
|
||||
percent = float(value) / endvalue
|
||||
arrow = '─' * int(round(percent * bar_length)-1) + '>'
|
||||
spaces = ' ' * (bar_length - len(arrow))
|
||||
|
||||
sys.stdout.write("\r┣{0}┫ {1}%".format(arrow + spaces, int(round(percent * 100))))
|
||||
sys.stdout.flush()
|
||||
|
||||
def getEpoch(self):
|
||||
'''returns epoch'''
|
||||
return math.floor(time.time())
|
||||
|
||||
def doPostRequest(self, url, data={}, port=0, proxyType='tor'):
|
||||
'''
|
||||
Do a POST request through a local tor or i2p instance
|
||||
'''
|
||||
if proxyType == 'tor':
|
||||
if port == 0:
|
||||
port = self._core.torPort
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
elif proxyType == 'i2p':
|
||||
proxies = {'http': 'http://127.0.0.1:4444'}
|
||||
else:
|
||||
return
|
||||
headers = {'user-agent': 'PyOnionr', 'Connection':'close'}
|
||||
try:
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
r = requests.post(url, data=data, headers=headers, proxies=proxies, allow_redirects=False, timeout=(15, 30))
|
||||
retData = r.text
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.debug('Error: %s' % str(e))
|
||||
retData = False
|
||||
return retData
|
||||
|
||||
def doGetRequest(self, url, port=0, proxyType='tor', ignoreAPI=False, returnHeaders=False):
|
||||
'''
|
||||
Do a get request through a local tor or i2p instance
|
||||
'''
|
||||
retData = False
|
||||
if proxyType == 'tor':
|
||||
if port == 0:
|
||||
raise onionrexceptions.MissingPort('Socks port required for Tor HTTP get request')
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
elif proxyType == 'i2p':
|
||||
proxies = {'http': 'http://127.0.0.1:4444'}
|
||||
else:
|
||||
return
|
||||
headers = {'user-agent': 'PyOnionr', 'Connection':'close'}
|
||||
response_headers = dict()
|
||||
try:
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
r = requests.get(url, headers=headers, proxies=proxies, allow_redirects=False, timeout=(15, 30), )
|
||||
# Check server is using same API version as us
|
||||
if not ignoreAPI:
|
||||
try:
|
||||
response_headers = r.headers
|
||||
if r.headers['X-API'] != str(API_VERSION):
|
||||
raise onionrexceptions.InvalidAPIVersion
|
||||
except KeyError:
|
||||
raise onionrexceptions.InvalidAPIVersion
|
||||
retData = r.text
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except ValueError as e:
|
||||
logger.debug('Failed to make GET request to %s' % url, error = e, sensitive = True)
|
||||
except onionrexceptions.InvalidAPIVersion:
|
||||
if 'X-API' in response_headers:
|
||||
logger.debug('Using API version %s. Cannot communicate with node\'s API version of %s.' % (API_VERSION, response_headers['X-API']))
|
||||
else:
|
||||
logger.debug('Using API version %s. API version was not sent with the request.' % API_VERSION)
|
||||
except requests.exceptions.RequestException as e:
|
||||
if not 'ConnectTimeoutError' in str(e) and not 'Request rejected or failed' in str(e):
|
||||
logger.debug('Error: %s' % str(e))
|
||||
retData = False
|
||||
if returnHeaders:
|
||||
return (retData, response_headers)
|
||||
else:
|
||||
return retData
|
||||
|
||||
@staticmethod
|
||||
def strToBytes(data):
|
||||
try:
|
||||
data = data.encode()
|
||||
except AttributeError:
|
||||
pass
|
||||
return data
|
||||
@staticmethod
|
||||
def bytesToStr(data):
|
||||
try:
|
||||
data = data.decode()
|
||||
except AttributeError:
|
||||
pass
|
||||
return data
|
||||
|
||||
def size(path='.'):
|
||||
'''
|
||||
Returns the size of a folder's contents in bytes
|
||||
'''
|
||||
total = 0
|
||||
if os.path.exists(path):
|
||||
if os.path.isfile(path):
|
||||
total = os.path.getsize(path)
|
||||
else:
|
||||
for entry in os.scandir(path):
|
||||
if entry.is_file():
|
||||
total += entry.stat().st_size
|
||||
elif entry.is_dir():
|
||||
total += size(entry.path)
|
||||
return total
|
||||
|
||||
def humanSize(num, suffix='B'):
|
||||
'''
|
||||
Converts from bytes to a human readable format.
|
||||
'''
|
||||
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||
if abs(num) < 1024.0:
|
||||
return "%.1f %s%s" % (num, unit, suffix)
|
||||
num /= 1024.0
|
||||
return "%.1f %s%s" % (num, 'Yi', suffix)
|
0
onionr/onionrutils/__init__.py
Normal file
0
onionr/onionrutils/__init__.py
Normal file
90
onionr/onionrutils/basicrequests.py
Normal file
90
onionr/onionrutils/basicrequests.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Do HTTP GET or POST requests through a proxy
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 requests
|
||||
import logger, onionrexceptions
|
||||
def do_post_request(core_inst, url, data={}, port=0, proxyType='tor'):
|
||||
'''
|
||||
Do a POST request through a local tor or i2p instance
|
||||
'''
|
||||
if proxyType == 'tor':
|
||||
if port == 0:
|
||||
port = core_inst.torPort
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
elif proxyType == 'i2p':
|
||||
proxies = {'http': 'http://127.0.0.1:4444'}
|
||||
else:
|
||||
return
|
||||
headers = {'user-agent': 'PyOnionr', 'Connection':'close'}
|
||||
try:
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
r = requests.post(url, data=data, headers=headers, proxies=proxies, allow_redirects=False, timeout=(15, 30))
|
||||
retData = r.text
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.debug('Error: %s' % str(e))
|
||||
retData = False
|
||||
return retData
|
||||
|
||||
def do_get_request(core_inst, url, port=0, proxyType='tor', ignoreAPI=False, returnHeaders=False):
|
||||
'''
|
||||
Do a get request through a local tor or i2p instance
|
||||
'''
|
||||
API_VERSION = core_inst.onionrInst.API_VERSION
|
||||
retData = False
|
||||
if proxyType == 'tor':
|
||||
if port == 0:
|
||||
raise onionrexceptions.MissingPort('Socks port required for Tor HTTP get request')
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
elif proxyType == 'i2p':
|
||||
proxies = {'http': 'http://127.0.0.1:4444'}
|
||||
else:
|
||||
return
|
||||
headers = {'user-agent': 'PyOnionr', 'Connection':'close'}
|
||||
response_headers = dict()
|
||||
try:
|
||||
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
|
||||
r = requests.get(url, headers=headers, proxies=proxies, allow_redirects=False, timeout=(15, 30), )
|
||||
# Check server is using same API version as us
|
||||
if not ignoreAPI:
|
||||
try:
|
||||
response_headers = r.headers
|
||||
if r.headers['X-API'] != str(API_VERSION):
|
||||
raise onionrexceptions.InvalidAPIVersion
|
||||
except KeyError:
|
||||
raise onionrexceptions.InvalidAPIVersion
|
||||
retData = r.text
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except ValueError as e:
|
||||
pass
|
||||
except onionrexceptions.InvalidAPIVersion:
|
||||
if 'X-API' in response_headers:
|
||||
logger.debug('Using API version %s. Cannot communicate with node\'s API version of %s.' % (API_VERSION, response_headers['X-API']))
|
||||
else:
|
||||
logger.debug('Using API version %s. API version was not sent with the request.' % API_VERSION)
|
||||
except requests.exceptions.RequestException as e:
|
||||
if not 'ConnectTimeoutError' in str(e) and not 'Request rejected or failed' in str(e):
|
||||
logger.debug('Error: %s' % str(e))
|
||||
retData = False
|
||||
if returnHeaders:
|
||||
return (retData, response_headers)
|
||||
else:
|
||||
return retData
|
108
onionr/onionrutils/blockmetadata.py
Normal file
108
onionr/onionrutils/blockmetadata.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Module to fetch block metadata from raw block data and process it
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 json, sqlite3
|
||||
import logger, onionrevents
|
||||
from onionrusers import onionrusers
|
||||
from etc import onionrvalues
|
||||
import onionrblockapi
|
||||
from . import epoch, stringvalidators, bytesconverter
|
||||
def get_block_metadata_from_data(blockData):
|
||||
'''
|
||||
accepts block contents as string, returns a tuple of
|
||||
metadata, meta (meta being internal metadata, which will be
|
||||
returned as an encrypted base64 string if it is encrypted, dict if not).
|
||||
'''
|
||||
meta = {}
|
||||
metadata = {}
|
||||
data = blockData
|
||||
try:
|
||||
blockData = blockData.encode()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
metadata = json.loads(blockData[:blockData.find(b'\n')].decode())
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
else:
|
||||
data = blockData[blockData.find(b'\n'):].decode()
|
||||
|
||||
if not metadata['encryptType'] in ('asym', 'sym'):
|
||||
try:
|
||||
meta = json.loads(metadata['meta'])
|
||||
except KeyError:
|
||||
pass
|
||||
meta = metadata['meta']
|
||||
return (metadata, meta, data)
|
||||
|
||||
def process_block_metadata(core_inst, blockHash):
|
||||
'''
|
||||
Read metadata from a block and cache it to the block database
|
||||
'''
|
||||
curTime = epoch.get_rounded_epoch(roundS=60)
|
||||
myBlock = onionrblockapi.Block(blockHash, core_inst)
|
||||
if myBlock.isEncrypted:
|
||||
myBlock.decrypt()
|
||||
if (myBlock.isEncrypted and myBlock.decrypted) or (not myBlock.isEncrypted):
|
||||
blockType = myBlock.getMetadata('type') # we would use myBlock.getType() here, but it is bugged with encrypted blocks
|
||||
|
||||
signer = bytesconverter.bytes_to_str(myBlock.signer)
|
||||
valid = myBlock.verifySig()
|
||||
if myBlock.getMetadata('newFSKey') is not None:
|
||||
onionrusers.OnionrUser(core_inst, signer).addForwardKey(myBlock.getMetadata('newFSKey'))
|
||||
|
||||
try:
|
||||
if len(blockType) <= 10:
|
||||
core_inst.updateBlockInfo(blockHash, 'dataType', blockType)
|
||||
except TypeError:
|
||||
logger.warn("Missing block information")
|
||||
pass
|
||||
# Set block expire time if specified
|
||||
try:
|
||||
expireTime = myBlock.getHeader('expire')
|
||||
assert len(str(int(expireTime))) < 20 # test that expire time is an integer of sane length (for epoch)
|
||||
except (AssertionError, ValueError, TypeError) as e:
|
||||
expireTime = onionrvalues.OnionrValues().default_expire + curTime
|
||||
finally:
|
||||
core_inst.updateBlockInfo(blockHash, 'expire', expireTime)
|
||||
if not blockType is None:
|
||||
core_inst.updateBlockInfo(blockHash, 'dataType', blockType)
|
||||
onionrevents.event('processblocks', data = {'block': myBlock, 'type': blockType, 'signer': signer, 'validSig': valid}, onionr = core_inst.onionrInst)
|
||||
else:
|
||||
pass
|
||||
|
||||
def has_block(core_inst, hash):
|
||||
'''
|
||||
Check for new block in the list
|
||||
'''
|
||||
conn = sqlite3.connect(core_inst.blockDB)
|
||||
c = conn.cursor()
|
||||
if not stringvalidators.validate_hash(hash):
|
||||
raise Exception("Invalid hash")
|
||||
for result in c.execute("SELECT COUNT() FROM hashes WHERE hash = ?", (hash,)):
|
||||
if result[0] >= 1:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return False
|
||||
return False
|
14
onionr/onionrutils/bytesconverter.py
Normal file
14
onionr/onionrutils/bytesconverter.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
def str_to_bytes(data):
|
||||
'''Converts a string to bytes with .encode()'''
|
||||
try:
|
||||
data = data.encode('UTF-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
return data
|
||||
|
||||
def bytes_to_str(data):
|
||||
try:
|
||||
data = data.decode('UTF-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
return data
|
38
onionr/onionrutils/checkcommunicator.py
Normal file
38
onionr/onionrutils/checkcommunicator.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Check if the communicator is running
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 time, os
|
||||
def is_communicator_running(core_inst, timeout = 5, interval = 0.1):
|
||||
try:
|
||||
runcheck_file = core_inst.dataDir + '.runcheck'
|
||||
|
||||
if not os.path.isfile(runcheck_file):
|
||||
open(runcheck_file, 'w+').close()
|
||||
|
||||
starttime = time.time()
|
||||
|
||||
while True:
|
||||
time.sleep(interval)
|
||||
|
||||
if not os.path.isfile(runcheck_file):
|
||||
return True
|
||||
elif time.time() - starttime >= timeout:
|
||||
return False
|
||||
except:
|
||||
return False
|
30
onionr/onionrutils/epoch.py
Normal file
30
onionr/onionrutils/epoch.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Get floored epoch, or rounded epoch
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 math, time
|
||||
def get_rounded_epoch(roundS=60):
|
||||
'''
|
||||
Returns the epoch, rounded down to given seconds (Default 60)
|
||||
'''
|
||||
epoch = get_epoch()
|
||||
return epoch - (epoch % roundS)
|
||||
|
||||
def get_epoch():
|
||||
'''returns epoch'''
|
||||
return math.floor(time.time())
|
10
onionr/onionrutils/escapeansi.py
Normal file
10
onionr/onionrutils/escapeansi.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
import re
|
||||
def escape_ANSI(line):
|
||||
'''
|
||||
Remove ANSI escape codes from a string with regex
|
||||
|
||||
adapted from: https://stackoverflow.com/a/38662876 by user https://stackoverflow.com/users/802365/%c3%89douard-lopez
|
||||
cc-by-sa-3 license https://creativecommons.org/licenses/by-sa/3.0/
|
||||
'''
|
||||
ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
|
||||
return ansi_escape.sub('', line)
|
29
onionr/onionrutils/getclientapiserver.py
Normal file
29
onionr/onionrutils/getclientapiserver.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Return the client api server address and port, which is usually random
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
def get_client_API_server(core_inst):
|
||||
retData = ''
|
||||
try:
|
||||
with open(core_inst.privateApiHostFile, 'r') as host:
|
||||
hostname = host.read()
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError
|
||||
else:
|
||||
retData += '%s:%s' % (hostname, core_inst.config.get('client.client.port'))
|
||||
return retData
|
48
onionr/onionrutils/importnewblocks.py
Normal file
48
onionr/onionrutils/importnewblocks.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
import new blocks from disk, providing transport agnosticism
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 glob
|
||||
import logger, core
|
||||
from onionrutils import blockmetadata
|
||||
def import_new_blocks(core_inst=None, scanDir=''):
|
||||
'''
|
||||
This function is intended to scan for new blocks ON THE DISK and import them
|
||||
'''
|
||||
if core_inst is None:
|
||||
core_inst = core.Core()
|
||||
blockList = core_inst.getBlockList()
|
||||
exist = False
|
||||
if scanDir == '':
|
||||
scanDir = core_inst.blockDataLocation
|
||||
if not scanDir.endswith('/'):
|
||||
scanDir += '/'
|
||||
for block in glob.glob(scanDir + "*.dat"):
|
||||
if block.replace(scanDir, '').replace('.dat', '') not in blockList:
|
||||
exist = True
|
||||
logger.info('Found new block on dist %s' % block, terminal=True)
|
||||
with open(block, 'rb') as newBlock:
|
||||
block = block.replace(scanDir, '').replace('.dat', '')
|
||||
if core_inst._crypto.sha3Hash(newBlock.read()) == block.replace('.dat', ''):
|
||||
core_inst.addToBlockDB(block.replace('.dat', ''), dataSaved=True)
|
||||
logger.info('Imported block %s.' % block, terminal=True)
|
||||
blockmetadata.process_block_metadata(core_inst, block)
|
||||
else:
|
||||
logger.warn('Failed to verify hash for %s' % block, terminal=True)
|
||||
if not exist:
|
||||
logger.info('No blocks found to import', terminal=True)
|
51
onionr/onionrutils/localcommand.py
Normal file
51
onionr/onionrutils/localcommand.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
send a command to the local API server
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 urllib, requests, time
|
||||
import logger
|
||||
from onionrutils import getclientapiserver
|
||||
def local_command(core_inst, command, data='', silent = True, post=False, postData = {}, maxWait=20):
|
||||
'''
|
||||
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
|
||||
'''
|
||||
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
|
||||
hostname = ''
|
||||
waited = 0
|
||||
while hostname == '':
|
||||
try:
|
||||
hostname = getclientapiserver.get_client_API_server(core_inst)
|
||||
except FileNotFoundError:
|
||||
time.sleep(1)
|
||||
waited += 1
|
||||
if waited == maxWait:
|
||||
return False
|
||||
if data != '':
|
||||
data = '&data=' + urllib.parse.quote_plus(data)
|
||||
payload = 'http://%s/%s%s' % (hostname, command, data)
|
||||
try:
|
||||
if post:
|
||||
retData = requests.post(payload, data=postData, headers={'token': core_inst.config.get('client.webpassword'), 'Connection':'close'}, timeout=(maxWait, maxWait)).text
|
||||
else:
|
||||
retData = requests.get(payload, headers={'token': core_inst.config.get('client.webpassword'), 'Connection':'close'}, timeout=(maxWait, maxWait)).text
|
||||
except Exception as error:
|
||||
if not silent:
|
||||
logger.error('Failed to make local request (command: %s):%s' % (command, error), terminal=True)
|
||||
retData = False
|
||||
|
||||
return retData
|
27
onionr/onionrutils/mnemonickeys.py
Normal file
27
onionr/onionrutils/mnemonickeys.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
convert a base32 string (intended for ed25519 user ids) to pgp word list
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 base64
|
||||
from etc import pgpwords
|
||||
def get_human_readable_ID(core_inst, pub=''):
|
||||
'''gets a human readable ID from a public key'''
|
||||
if pub == '':
|
||||
pub = core_inst._crypto.pubKey
|
||||
pub = base64.b16encode(base64.b32decode(pub)).decode()
|
||||
return ' '.join(pgpwords.wordify(pub))
|
119
onionr/onionrutils/stringvalidators.py
Normal file
119
onionr/onionrutils/stringvalidators.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
validate various string data types
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 base64, string
|
||||
import unpaddedbase32, nacl.signing, nacl.encoding
|
||||
from onionrutils import bytesconverter
|
||||
def validate_hash(data, length=64):
|
||||
'''
|
||||
Validate if a string is a valid hash hex digest (does not compare, just checks length and charset)
|
||||
|
||||
Length is only invalid if its *more* than the specified
|
||||
'''
|
||||
retVal = True
|
||||
if data == False or data == True:
|
||||
return False
|
||||
data = data.strip()
|
||||
if len(data) > length:
|
||||
retVal = False
|
||||
else:
|
||||
try:
|
||||
int(data, 16)
|
||||
except ValueError:
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
|
||||
def validate_pub_key(key):
|
||||
'''
|
||||
Validate if a string is a valid base32 encoded Ed25519 key
|
||||
'''
|
||||
if type(key) is type(None):
|
||||
return False
|
||||
# Accept keys that have no = padding
|
||||
key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
|
||||
|
||||
retVal = False
|
||||
try:
|
||||
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
|
||||
except nacl.exceptions.ValueError:
|
||||
pass
|
||||
except base64.binascii.Error as err:
|
||||
pass
|
||||
else:
|
||||
retVal = True
|
||||
return retVal
|
||||
|
||||
def validate_transport(id):
|
||||
try:
|
||||
idLength = len(id)
|
||||
retVal = True
|
||||
idNoDomain = ''
|
||||
peerType = ''
|
||||
# i2p b32 addresses are 60 characters long (including .b32.i2p)
|
||||
if idLength == 60:
|
||||
peerType = 'i2p'
|
||||
if not id.endswith('.b32.i2p'):
|
||||
retVal = False
|
||||
else:
|
||||
idNoDomain = id.split('.b32.i2p')[0]
|
||||
# Onion v2's are 22 (including .onion), v3's are 62 with .onion
|
||||
elif idLength == 22 or idLength == 62:
|
||||
peerType = 'onion'
|
||||
if not id.endswith('.onion'):
|
||||
retVal = False
|
||||
else:
|
||||
idNoDomain = id.split('.onion')[0]
|
||||
else:
|
||||
retVal = False
|
||||
if retVal:
|
||||
if peerType == 'i2p':
|
||||
try:
|
||||
id.split('.b32.i2p')[2]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
retVal = False
|
||||
elif peerType == 'onion':
|
||||
try:
|
||||
id.split('.onion')[2]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
retVal = False
|
||||
if not idNoDomain.isalnum():
|
||||
retVal = False
|
||||
|
||||
# Validate address is valid base32 (when capitalized and minus extension); v2/v3 onions and .b32.i2p use base32
|
||||
for x in idNoDomain.upper():
|
||||
if x not in string.ascii_uppercase and x not in '234567':
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def is_integer_string(data):
|
||||
'''Check if a string is a valid base10 integer (also returns true if already an int)'''
|
||||
try:
|
||||
int(data)
|
||||
except (ValueError, TypeError) as e:
|
||||
return False
|
||||
else:
|
||||
return True
|
97
onionr/onionrutils/validatemetadata.py
Normal file
97
onionr/onionrutils/validatemetadata.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
validate new block's metadata
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 json
|
||||
import logger, onionrexceptions
|
||||
from etc import onionrvalues
|
||||
from onionrutils import stringvalidators, epoch, bytesconverter
|
||||
def validate_metadata(core_inst, metadata, blockData):
|
||||
'''Validate metadata meets onionr spec (does not validate proof value computation), take in either dictionary or json string'''
|
||||
# TODO, make this check sane sizes
|
||||
retData = False
|
||||
maxClockDifference = 120
|
||||
|
||||
# convert to dict if it is json string
|
||||
if type(metadata) is str:
|
||||
try:
|
||||
metadata = json.loads(metadata)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# Validate metadata dict for invalid keys to sizes that are too large
|
||||
maxAge = core_inst.config.get("general.max_block_age", onionrvalues.OnionrValues().default_expire)
|
||||
if type(metadata) is dict:
|
||||
for i in metadata:
|
||||
try:
|
||||
core_inst.requirements.blockMetadataLengths[i]
|
||||
except KeyError:
|
||||
logger.warn('Block has invalid metadata key ' + i)
|
||||
break
|
||||
else:
|
||||
testData = metadata[i]
|
||||
try:
|
||||
testData = len(testData)
|
||||
except (TypeError, AttributeError) as e:
|
||||
testData = len(str(testData))
|
||||
if core_inst.requirements.blockMetadataLengths[i] < testData:
|
||||
logger.warn('Block metadata key ' + i + ' exceeded maximum size')
|
||||
break
|
||||
if i == 'time':
|
||||
if not stringvalidators.is_integer_string(metadata[i]):
|
||||
logger.warn('Block metadata time stamp is not integer string or int')
|
||||
break
|
||||
isFuture = (metadata[i] - epoch.get_epoch())
|
||||
if isFuture > maxClockDifference:
|
||||
logger.warn('Block timestamp is skewed to the future over the max %s: %s' (maxClockDifference, isFuture))
|
||||
break
|
||||
if (epoch.get_epoch() - metadata[i]) > maxAge:
|
||||
logger.warn('Block is outdated: %s' % (metadata[i],))
|
||||
break
|
||||
elif i == 'expire':
|
||||
try:
|
||||
assert int(metadata[i]) > epoch.get_epoch()
|
||||
except AssertionError:
|
||||
logger.warn('Block is expired: %s less than %s' % (metadata[i], epoch.get_epoch()))
|
||||
break
|
||||
elif i == 'encryptType':
|
||||
try:
|
||||
assert metadata[i] in ('asym', 'sym', '')
|
||||
except AssertionError:
|
||||
logger.warn('Invalid encryption mode')
|
||||
break
|
||||
else:
|
||||
# if metadata loop gets no errors, it does not break, therefore metadata is valid
|
||||
# make sure we do not have another block with the same data content (prevent data duplication and replay attacks)
|
||||
nonce = bytesconverter.bytes_to_str(core_inst._crypto.sha3Hash(blockData))
|
||||
try:
|
||||
with open(core_inst.dataNonceFile, 'r') as nonceFile:
|
||||
if nonce in nonceFile.read():
|
||||
retData = False # we've seen that nonce before, so we can't pass metadata
|
||||
raise onionrexceptions.DataExists
|
||||
except FileNotFoundError:
|
||||
retData = True
|
||||
except onionrexceptions.DataExists:
|
||||
# do not set retData to True, because nonce has been seen before
|
||||
pass
|
||||
else:
|
||||
retData = True
|
||||
else:
|
||||
logger.warn('In call to utils.validateMetadata, metadata must be JSON string or a dictionary object')
|
||||
|
||||
return retData
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This is an interactive menu-driven CLI interface for Onionr
|
||||
'''
|
||||
|
@ -23,6 +23,7 @@ import threading, time, uuid, subprocess, sys
|
|||
import config, logger
|
||||
from onionrblockapi import Block
|
||||
import onionrplugins
|
||||
from onionrutils import localcommand
|
||||
|
||||
plugin_name = 'cliui'
|
||||
PLUGIN_VERSION = '0.0.1'
|
||||
|
@ -48,7 +49,7 @@ class OnionrCLIUI:
|
|||
|
||||
def isRunning(self):
|
||||
while not self.shutdown:
|
||||
if self.myCore._utils.localCommand('ping', maxWait=5) == 'pong!':
|
||||
if localcommand.local_command(self.myCore, 'ping', maxWait=5) == 'pong!':
|
||||
self.running = 'Yes'
|
||||
else:
|
||||
self.running = 'No'
|
||||
|
@ -100,7 +101,7 @@ class OnionrCLIUI:
|
|||
elif choice == "":
|
||||
pass
|
||||
else:
|
||||
logger.error("Invalid choice")
|
||||
logger.error("Invalid choice", terminal=True)
|
||||
return
|
||||
|
||||
def on_init(api, data = None):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This is an interactive menu-driven CLI interface for Onionr
|
||||
'''
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This default plugin allows users to encrypt/decrypt messages without using blocks
|
||||
'''
|
||||
|
@ -21,6 +21,7 @@
|
|||
# Imports some useful libraries
|
||||
import logger, config, threading, time, datetime, sys, json
|
||||
from onionrblockapi import Block
|
||||
from onionrutils import stringvalidators
|
||||
import onionrexceptions, onionrusers
|
||||
import locale
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
@ -43,16 +44,16 @@ class PlainEncryption:
|
|||
pass
|
||||
|
||||
try:
|
||||
if not self.api.get_core()._utils.validatePubKey(sys.argv[2]):
|
||||
if not stringvalidators.validate_pub_key(sys.argv[2]):
|
||||
raise onionrexceptions.InvalidPubkey
|
||||
except (ValueError, IndexError) as e:
|
||||
logger.error("Peer public key not specified")
|
||||
logger.error("Peer public key not specified", terminal=True)
|
||||
except onionrexceptions.InvalidPubkey:
|
||||
logger.error("Invalid public key")
|
||||
logger.error("Invalid public key", terminal=True)
|
||||
else:
|
||||
pubkey = sys.argv[2]
|
||||
# Encrypt if public key is valid
|
||||
logger.info("Please enter your message (ctrl-d or -q to stop):")
|
||||
logger.info("Please enter your message (ctrl-d or -q to stop):", terminal=True)
|
||||
try:
|
||||
for line in sys.stdin:
|
||||
if line == '-q\n':
|
||||
|
@ -72,12 +73,12 @@ class PlainEncryption:
|
|||
plaintext = data
|
||||
encrypted = self.api.get_core()._crypto.pubKeyEncrypt(plaintext, pubkey, encodedData=True)
|
||||
encrypted = self.api.get_core()._utils.bytesToStr(encrypted)
|
||||
logger.info('Encrypted Message: \n\nONIONR ENCRYPTED DATA %s END ENCRYPTED DATA' % (encrypted,))
|
||||
logger.info('Encrypted Message: \n\nONIONR ENCRYPTED DATA %s END ENCRYPTED DATA' % (encrypted,), terminal=True)
|
||||
|
||||
def decrypt(self):
|
||||
plaintext = ""
|
||||
data = ""
|
||||
logger.info("Please enter your message (ctrl-d or -q to stop):")
|
||||
logger.info("Please enter your message (ctrl-d or -q to stop):", terminal=True)
|
||||
try:
|
||||
for line in sys.stdin:
|
||||
if line == '-q\n':
|
||||
|
@ -91,17 +92,17 @@ class PlainEncryption:
|
|||
myPub = self.api.get_core()._crypto.pubKey
|
||||
decrypted = self.api.get_core()._crypto.pubKeyDecrypt(encrypted, privkey=self.api.get_core()._crypto.privKey, encodedData=True)
|
||||
if decrypted == False:
|
||||
logger.error("Decryption failed")
|
||||
logger.error("Decryption failed", terminal=True)
|
||||
else:
|
||||
data = json.loads(decrypted)
|
||||
logger.info('Decrypted Message: \n\n%s' % data['data'])
|
||||
logger.info('Decrypted Message: \n\n%s' % data['data'], terminal=True)
|
||||
try:
|
||||
logger.info("Signing public key: %s" % (data['signer'],))
|
||||
logger.info("Signing public key: %s" % (data['signer'],), terminal=True)
|
||||
assert self.api.get_core()._crypto.edVerify(data['data'], data['signer'], data['sig']) != False
|
||||
except (AssertionError, KeyError) as e:
|
||||
logger.warn("WARNING: THIS MESSAGE HAS A MISSING OR INVALID SIGNATURE")
|
||||
logger.warn("WARNING: THIS MESSAGE HAS A MISSING OR INVALID SIGNATURE", terminal=True)
|
||||
else:
|
||||
logger.info("Message has good signature.")
|
||||
logger.info("Message has good signature.", terminal=True)
|
||||
return
|
||||
|
||||
def on_init(api, data = None):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
HTTP endpoints for controlling IMs
|
||||
'''
|
||||
|
@ -22,13 +22,13 @@ from flask import Response, request, redirect, Blueprint, send_from_directory
|
|||
import core
|
||||
|
||||
core_inst = core.Core()
|
||||
flask_blueprint = Blueprint('clandestine_control', __name__)
|
||||
flask_blueprint = Blueprint('esoteric_control', __name__)
|
||||
|
||||
@flask_blueprint.route('/clandestine/ping')
|
||||
@flask_blueprint.route('/esoteric/ping')
|
||||
def ping():
|
||||
return 'pong!'
|
||||
|
||||
@flask_blueprint.route('/clandestine/send/<peer>', methods=['POST'])
|
||||
@flask_blueprint.route('/esoteric/send/<peer>', methods=['POST'])
|
||||
def send_message(peer):
|
||||
data = request.get_json(force=True)
|
||||
core_inst.keyStore.refresh()
|
||||
|
@ -40,14 +40,14 @@ def send_message(peer):
|
|||
core_inst.keyStore.flush()
|
||||
return Response('success')
|
||||
|
||||
@flask_blueprint.route('/clandestine/gets/<peer>')
|
||||
@flask_blueprint.route('/esoteric/gets/<peer>')
|
||||
def get_sent(peer):
|
||||
sent = core_inst.keyStore.get('s' + peer)
|
||||
if sent is None:
|
||||
sent = []
|
||||
return Response(json.dumps(sent))
|
||||
|
||||
@flask_blueprint.route('/clandestine/addrec/<peer>', methods=['POST'])
|
||||
@flask_blueprint.route('/esoteric/addrec/<peer>', methods=['POST'])
|
||||
def add_rec(peer):
|
||||
data = request.get_json(force=True)
|
||||
core_inst.keyStore.refresh()
|
||||
|
@ -59,7 +59,7 @@ def add_rec(peer):
|
|||
core_inst.keyStore.flush()
|
||||
return Response('success')
|
||||
|
||||
@flask_blueprint.route('/clandestine/getrec/<peer>')
|
||||
@flask_blueprint.route('/esoteric/getrec/<peer>')
|
||||
def get_messages(peer):
|
||||
core_inst.keyStore.refresh()
|
||||
existing = core_inst.keyStore.get('r' + peer)
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name" : "clandestine",
|
||||
"name" : "esoteric",
|
||||
"version" : "1.0",
|
||||
"author" : "onionr"
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Instant message conversations with Onionr peers
|
||||
'''
|
||||
|
@ -23,8 +23,9 @@ import locale, sys, os, threading, json
|
|||
locale.setlocale(locale.LC_ALL, '')
|
||||
import onionrservices, logger
|
||||
from onionrservices import bootstrapservice
|
||||
from onionrutils import stringvalidators, epoch, basicrequests
|
||||
|
||||
plugin_name = 'clandestine'
|
||||
plugin_name = 'esoteric'
|
||||
PLUGIN_VERSION = '0.0.0'
|
||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||
import controlapi, peerserver
|
||||
|
@ -36,7 +37,7 @@ def exit_with_error(text=''):
|
|||
logger.error(text)
|
||||
sys.exit(1)
|
||||
|
||||
class Clandestine:
|
||||
class Esoteric:
|
||||
def __init__(self, pluginapi):
|
||||
self.myCore = pluginapi.get_core()
|
||||
self.peer = None
|
||||
|
@ -57,8 +58,8 @@ class Clandestine:
|
|||
else:
|
||||
message += '\n'
|
||||
except EOFError:
|
||||
message = json.dumps({'m': message, 't': self.myCore._utils.getEpoch()})
|
||||
print(self.myCore._utils.doPostRequest('http://%s/clandestine/sendto' % (self.transport,), port=self.socks, data=message))
|
||||
message = json.dumps({'m': message, 't': epoch.get_epoch()})
|
||||
print(basicrequests.do_post_request(self.myCore, 'http://%s/esoteric/sendto' % (self.transport,), port=self.socks, data=message))
|
||||
message = ''
|
||||
except KeyboardInterrupt:
|
||||
self.shutdown = True
|
||||
|
@ -66,7 +67,7 @@ class Clandestine:
|
|||
def create(self):
|
||||
try:
|
||||
peer = sys.argv[2]
|
||||
if not self.myCore._utils.validatePubKey(peer):
|
||||
if not stringvalidators.validate_pub_key(peer):
|
||||
exit_with_error('Invalid public key specified')
|
||||
except IndexError:
|
||||
exit_with_error('You must specify a peer public key')
|
||||
|
@ -77,7 +78,7 @@ class Clandestine:
|
|||
self.socks = self.myCore.config.get('tor.socksport')
|
||||
|
||||
print('connected with', peer, 'on', peer_transport_address)
|
||||
if self.myCore._utils.doGetRequest('http://%s/ping' % (peer_transport_address,), ignoreAPI=True, port=self.socks) == 'pong!':
|
||||
if basicrequests.do_get_request(self.myCore, 'http://%s/ping' % (peer_transport_address,), ignoreAPI=True, port=self.socks) == 'pong!':
|
||||
print('connected', peer_transport_address)
|
||||
threading.Thread(target=self._sender_loop).start()
|
||||
|
||||
|
@ -89,6 +90,6 @@ def on_init(api, data = None):
|
|||
'''
|
||||
|
||||
pluginapi = api
|
||||
chat = Clandestine(pluginapi)
|
||||
api.commands.register(['clandestine'], chat.create)
|
||||
chat = Esoteric(pluginapi)
|
||||
api.commands.register(['esoteric'], chat.create)
|
||||
return
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
HTTP endpoints for communicating with peers
|
||||
'''
|
||||
|
@ -19,9 +19,10 @@
|
|||
'''
|
||||
import sys, os, json
|
||||
import core
|
||||
from onionrutils import localcommand
|
||||
from flask import Response, request, redirect, Blueprint, abort, g
|
||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||
direct_blueprint = Blueprint('clandestine', __name__)
|
||||
direct_blueprint = Blueprint('esoteric', __name__)
|
||||
core_inst = core.Core()
|
||||
|
||||
storage_dir = core_inst.dataDir
|
||||
|
@ -35,11 +36,11 @@ def request_setup():
|
|||
g.host = host
|
||||
g.peer = core_inst.keyStore.get('dc-' + g.host)
|
||||
|
||||
@direct_blueprint.route('/clandestine/ping')
|
||||
@direct_blueprint.route('/esoteric/ping')
|
||||
def pingdirect():
|
||||
return 'pong!'
|
||||
|
||||
@direct_blueprint.route('/clandestine/sendto', methods=['POST', 'GET'])
|
||||
@direct_blueprint.route('/esoteric/sendto', methods=['POST', 'GET'])
|
||||
def sendto():
|
||||
try:
|
||||
msg = request.get_json(force=True)
|
||||
|
@ -47,9 +48,9 @@ def sendto():
|
|||
msg = ''
|
||||
else:
|
||||
msg = json.dumps(msg)
|
||||
core_inst._utils.localCommand('/clandestine/addrec/%s' % (g.peer,), post=True, postData=msg)
|
||||
localcommand.local_command(core_inst, '/esoteric/addrec/%s' % (g.peer,), post=True, postData=msg)
|
||||
return Response('success')
|
||||
|
||||
@direct_blueprint.route('/clandestine/poll')
|
||||
@direct_blueprint.route('/esoteric/poll')
|
||||
def poll_chat():
|
||||
return Response(core_inst._utils.localCommand('/clandestine/gets/%s' % (g.peer,)))
|
||||
return Response(localcommand.local_command(core_inst, '/esoteric/gets/%s' % (g.peer,)))
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file primarily serves to allow specific fetching of flow board messages
|
||||
'''
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This default plugin handles "flow" messages (global chatroom style communication)
|
||||
'''
|
||||
|
@ -22,6 +22,7 @@
|
|||
import threading, time, locale, sys, os
|
||||
from onionrblockapi import Block
|
||||
import logger, config
|
||||
from onionrutils import escapeansi, epoch
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||
|
@ -40,10 +41,10 @@ class OnionrFlow:
|
|||
return
|
||||
|
||||
def start(self):
|
||||
logger.warn("Please note: everything said here is public, even if a random channel name is used.")
|
||||
logger.warn("Please note: everything said here is public, even if a random channel name is used.", terminal=True)
|
||||
message = ""
|
||||
self.flowRunning = True
|
||||
newThread = threading.Thread(target=self.showOutput)
|
||||
newThread = threading.Thread(target=self.showOutput, daemon=True)
|
||||
newThread.start()
|
||||
try:
|
||||
self.channel = logger.readline("Enter a channel name or none for default:")
|
||||
|
@ -59,11 +60,12 @@ class OnionrFlow:
|
|||
else:
|
||||
if message == "q":
|
||||
self.flowRunning = False
|
||||
expireTime = self.myCore._utils.getEpoch() + 43200
|
||||
expireTime = epoch.get_epoch() + 43200
|
||||
if len(message) > 0:
|
||||
logger.info('Inserting message as block...', terminal=True)
|
||||
self.myCore.insertBlock(message, header='txt', expire=expireTime, meta={'ch': self.channel})
|
||||
|
||||
logger.info("Flow is exiting, goodbye")
|
||||
logger.info("Flow is exiting, goodbye", terminal=True)
|
||||
return
|
||||
|
||||
def showOutput(self):
|
||||
|
@ -74,18 +76,16 @@ class OnionrFlow:
|
|||
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
|
||||
logger.info('\n------------------------', prompt = False)
|
||||
logger.info('\n------------------------', prompt = False, terminal=True)
|
||||
content = block.getContent()
|
||||
# Escape new lines, remove trailing whitespace, and escape ansi sequences
|
||||
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)
|
||||
content = escapeansi.escape_ANSI(content.replace('\n', '\\n').replace('\r', '\\r').strip())
|
||||
logger.info(block.getDate().strftime("%m/%d %H:%M") + ' - ' + logger.colors.reset + content, prompt = False, terminal=True)
|
||||
self.alreadyOutputed.append(block.getHash())
|
||||
time.sleep(5)
|
||||
except KeyboardInterrupt:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This processes metadata for Onionr blocks
|
||||
'''
|
||||
|
@ -23,6 +23,7 @@ import logger, config
|
|||
import os, sys, json, time, random, shutil, base64, getpass, datetime, re
|
||||
from onionrblockapi import Block
|
||||
import onionrusers, onionrexceptions
|
||||
from onionrutils import stringvalidators
|
||||
|
||||
plugin_name = 'metadataprocessor'
|
||||
|
||||
|
@ -36,7 +37,7 @@ def _processForwardKey(api, myBlock):
|
|||
key = myBlock.getMetadata('newFSKey')
|
||||
|
||||
# We don't need to validate here probably, but it helps
|
||||
if api.get_utils().validatePubKey(key):
|
||||
if stringvalidators.validate_pub_key(key):
|
||||
peer.addForwardKey(key)
|
||||
else:
|
||||
raise onionrexceptions.InvalidPubkey("%s is not a valid pubkey key" % (key,))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network.
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This plugin acts as a plugin manager, and allows the user to install other plugins distributed over Onionr.
|
||||
'''
|
||||
|
@ -22,6 +22,7 @@
|
|||
import logger, config
|
||||
import os, sys, json, time, random, shutil, base64, getpass, datetime, re
|
||||
from onionrblockapi import Block
|
||||
from onionrutils import importnewblocks, stringvalidators
|
||||
|
||||
plugin_name = 'pluginmanager'
|
||||
|
||||
|
@ -180,11 +181,11 @@ def blockToPlugin(block):
|
|||
shutil.unpack_archive(source, destination)
|
||||
pluginapi.plugins.enable(name)
|
||||
|
||||
logger.info('Installation of %s complete.' % name)
|
||||
logger.info('Installation of %s complete.' % name, terminal=True)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error('Failed to install plugin.', error = e, timestamp = False)
|
||||
logger.error('Failed to install plugin.', error = e, timestamp = False, terminal=True)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -236,13 +237,13 @@ def pluginToBlock(plugin, import_block = True):
|
|||
# hash = pluginapi.get_core().insertBlock(, header = 'plugin', sign = True)
|
||||
|
||||
if import_block:
|
||||
pluginapi.get_utils().importNewBlocks()
|
||||
importnewblocks.import_new_blocks(pluginapi.get_core())
|
||||
|
||||
return hash
|
||||
else:
|
||||
logger.error('Plugin %s does not exist.' % plugin)
|
||||
logger.error('Plugin %s does not exist.' % plugin, terminal=True)
|
||||
except Exception as e:
|
||||
logger.error('Failed to convert plugin to block.', error = e, timestamp = False)
|
||||
logger.error('Failed to convert plugin to block.', error = e, timestamp = False, terminal=True)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -261,7 +262,7 @@ def installBlock(block):
|
|||
|
||||
install = False
|
||||
|
||||
logger.info(('Will install %s' + (' v' + version if not version is None else '') + ' (%s), by %s') % (name, date, author))
|
||||
logger.info(('Will install %s' + (' v' + version if not version is None else '') + ' (%s), by %s') % (name, date, author), terminal=True)
|
||||
|
||||
# TODO: Convert to single line if statement
|
||||
if os.path.exists(pluginapi.plugins.get_folder(name)):
|
||||
|
@ -273,12 +274,12 @@ def installBlock(block):
|
|||
blockToPlugin(block.getHash())
|
||||
addPlugin(name)
|
||||
else:
|
||||
logger.info('Installation cancelled.')
|
||||
logger.info('Installation cancelled.', terminal=True)
|
||||
return False
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error('Failed to install plugin.', error = e, timestamp = False)
|
||||
logger.error('Failed to install plugin.', error = e, timestamp = False, terminal=True)
|
||||
return False
|
||||
|
||||
def uninstallPlugin(plugin):
|
||||
|
@ -291,12 +292,12 @@ def uninstallPlugin(plugin):
|
|||
remove = False
|
||||
|
||||
if not exists:
|
||||
logger.warn('Plugin %s does not exist.' % plugin, timestamp = False)
|
||||
logger.warn('Plugin %s does not exist.' % plugin, timestamp = False, terminal=True)
|
||||
return False
|
||||
|
||||
default = 'y'
|
||||
if not installedByPluginManager:
|
||||
logger.warn('The plugin %s was not installed by %s.' % (plugin, plugin_name), timestamp = False)
|
||||
logger.warn('The plugin %s was not installed by %s.' % (plugin, plugin_name), timestamp = False, terminal=True)
|
||||
default = 'n'
|
||||
remove = logger.confirm(message = 'All plugin data will be lost. Are you sure you want to proceed %s?', default = default)
|
||||
|
||||
|
@ -306,20 +307,20 @@ def uninstallPlugin(plugin):
|
|||
pluginapi.plugins.disable(plugin)
|
||||
shutil.rmtree(pluginFolder)
|
||||
|
||||
logger.info('Uninstallation of %s complete.' % plugin)
|
||||
logger.info('Uninstallation of %s complete.' % plugin, terminal=True)
|
||||
|
||||
return True
|
||||
else:
|
||||
logger.info('Uninstallation cancelled.')
|
||||
except Exception as e:
|
||||
logger.error('Failed to uninstall plugin.', error = e)
|
||||
logger.error('Failed to uninstall plugin.', error = e, terminal=True)
|
||||
return False
|
||||
|
||||
# command handlers
|
||||
|
||||
def help():
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]', terminal=True)
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]', terminal=True)
|
||||
|
||||
def commandInstallPlugin():
|
||||
if len(sys.argv) >= 3:
|
||||
|
@ -345,20 +346,20 @@ def commandInstallPlugin():
|
|||
|
||||
if pkobh is None:
|
||||
# still nothing found, try searching repositories
|
||||
logger.info('Searching for public key in repositories...')
|
||||
logger.info('Searching for public key in repositories...', terminal=True)
|
||||
try:
|
||||
repos = getRepositories()
|
||||
distributors = list()
|
||||
for repo, records in repos.items():
|
||||
if pluginname in records:
|
||||
logger.debug('Found %s in repository %s for plugin %s.' % (records[pluginname], repo, pluginname))
|
||||
logger.debug('Found %s in repository %s for plugin %s.' % (records[pluginname], repo, pluginname), terminal=True)
|
||||
distributors.append(records[pluginname])
|
||||
|
||||
if len(distributors) != 0:
|
||||
distributor = None
|
||||
|
||||
if len(distributors) == 1:
|
||||
logger.info('Found distributor: %s' % distributors[0])
|
||||
logger.info('Found distributor: %s' % distributors[0], terminal=True)
|
||||
distributor = distributors[0]
|
||||
else:
|
||||
distributors_message = ''
|
||||
|
@ -368,11 +369,11 @@ def commandInstallPlugin():
|
|||
distributors_message += ' ' + logger.colors.bold + str(index) + ') ' + logger.colors.reset + str(dist) + '\n'
|
||||
index += 1
|
||||
|
||||
logger.info((logger.colors.bold + 'Found distributors (%s):' + logger.colors.reset + '\n' + distributors_message) % len(distributors))
|
||||
logger.info((logger.colors.bold + 'Found distributors (%s):' + logger.colors.reset + '\n' + distributors_message) % len(distributors), terminal=True)
|
||||
|
||||
valid = False
|
||||
while not valid:
|
||||
choice = logger.readline('Select the number of the key to use, from 1 to %s, or press Ctrl+C to cancel:' % (index - 1))
|
||||
choice = logger.readline('Select the number of the key to use, from 1 to %s, or press Ctrl+C to cancel:' % (index - 1), terminal=True)
|
||||
|
||||
try:
|
||||
choice = int(choice)
|
||||
|
@ -380,7 +381,7 @@ def commandInstallPlugin():
|
|||
distributor = distributors[int(choice)]
|
||||
valid = True
|
||||
except KeyboardInterrupt:
|
||||
logger.info('Installation cancelled.')
|
||||
logger.info('Installation cancelled.', terminal=True)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
@ -388,42 +389,42 @@ def commandInstallPlugin():
|
|||
if not distributor is None:
|
||||
pkobh = distributor
|
||||
except Exception as e:
|
||||
logger.warn('Failed to lookup plugin in repositories.', timestamp = False)
|
||||
logger.warn('Failed to lookup plugin in repositories.', timestamp = False, terminal=True)
|
||||
return True
|
||||
|
||||
if pkobh is None:
|
||||
logger.error('No key for this plugin found in keystore or repositories, please specify.', timestamp = False)
|
||||
logger.error('No key for this plugin found in keystore or repositories, please specify.', timestamp = False, terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
valid_hash = pluginapi.get_utils().validateHash(pkobh)
|
||||
valid_hash = stringvalidators.validate_hash(pkobh)
|
||||
real_block = False
|
||||
valid_key = pluginapi.get_utils().validatePubKey(pkobh)
|
||||
valid_key = stringvalidators.validate_pub_key(pkobh)
|
||||
real_key = False
|
||||
|
||||
if valid_hash:
|
||||
real_block = Block.exists(pkobh)
|
||||
elif valid_key:
|
||||
real_key = pluginapi.get_utils().hasKey(pkobh)
|
||||
real_key = pkobh in pluginapi.get_core().listPeers()
|
||||
|
||||
blockhash = None
|
||||
|
||||
if valid_hash and not real_block:
|
||||
logger.error('Block hash not found. Perhaps it has not been synced yet?', timestamp = False)
|
||||
logger.debug('Is valid hash, but does not belong to a known block.')
|
||||
logger.error('Block hash not found. Perhaps it has not been synced yet?', timestamp = False, terminal=True)
|
||||
logger.debug('Is valid hash, but does not belong to a known block.', terminal=True)
|
||||
|
||||
return True
|
||||
elif valid_hash and real_block:
|
||||
blockhash = str(pkobh)
|
||||
logger.debug('Using block %s...' % blockhash)
|
||||
logger.debug('Using block %s...' % blockhash, terminal=True)
|
||||
|
||||
installBlock(blockhash)
|
||||
elif valid_key and not real_key:
|
||||
logger.error('Public key not found. Try adding the node by address manually, if possible.', timestamp = False)
|
||||
logger.debug('Is valid key, but the key is not a known one.')
|
||||
logger.error('Public key not found. Try adding the node by address manually, if possible.', timestamp = False, terminal=True)
|
||||
logger.debug('Is valid key, but the key is not a known one.', terminal=True)
|
||||
elif valid_key and real_key:
|
||||
publickey = str(pkobh)
|
||||
logger.debug('Using public key %s...' % publickey)
|
||||
logger.debug('Using public key %s...' % publickey, terminal=True)
|
||||
|
||||
saveKey(pluginname, pkobh)
|
||||
|
||||
|
@ -455,14 +456,14 @@ def commandInstallPlugin():
|
|||
except Exception as e:
|
||||
pass
|
||||
|
||||
logger.warn('Only continue the installation if you are absolutely certain that you trust the plugin distributor. Public key of plugin distributor: %s' % publickey, timestamp = False)
|
||||
logger.debug('Most recent block matching parameters is %s' % mostRecentVersionBlock)
|
||||
logger.warn('Only continue the installation if you are absolutely certain that you trust the plugin distributor. Public key of plugin distributor: %s' % publickey, timestamp = False, terminal=True)
|
||||
logger.debug('Most recent block matching parameters is %s' % mostRecentVersionBlock, terminal=True)
|
||||
installBlock(mostRecentVersionBlock)
|
||||
else:
|
||||
logger.error('Unknown data "%s"; must be public key or block hash.' % str(pkobh), timestamp = False)
|
||||
logger.error('Unknown data "%s"; must be public key or block hash.' % str(pkobh), timestamp = False, terminal=True)
|
||||
return
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]', terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -470,12 +471,12 @@ def commandUninstallPlugin():
|
|||
if len(sys.argv) >= 3:
|
||||
uninstallPlugin(sys.argv[2])
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin>')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin>', terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
def commandSearchPlugin():
|
||||
logger.info('This feature has not been created yet. Please check back later.')
|
||||
logger.info('This feature has not been created yet. Please check back later.', terminal=True)
|
||||
return True
|
||||
|
||||
def commandAddRepository():
|
||||
|
@ -484,7 +485,7 @@ def commandAddRepository():
|
|||
|
||||
blockhash = sys.argv[2]
|
||||
|
||||
if pluginapi.get_utils().validateHash(blockhash):
|
||||
if stringvalidators.validate_hash(blockhash):
|
||||
if Block.exists(blockhash):
|
||||
try:
|
||||
blockContent = json.loads(Block(blockhash, core = pluginapi.get_core()).getContent())
|
||||
|
@ -492,25 +493,25 @@ def commandAddRepository():
|
|||
pluginslist = dict()
|
||||
|
||||
for pluginname, distributor in blockContent['plugins']:
|
||||
if pluginapi.get_utils().validatePubKey(distributor):
|
||||
if stringvalidators.validate_pub_key(distributor):
|
||||
pluginslist[pluginname] = distributor
|
||||
|
||||
logger.debug('Found %s records in repository.' % len(pluginslist))
|
||||
logger.debug('Found %s records in repository.' % len(pluginslist), terminal=True)
|
||||
|
||||
if len(pluginslist) != 0:
|
||||
addRepository(blockhash, pluginslist)
|
||||
logger.info('Successfully added repository.')
|
||||
logger.info('Successfully added repository.', terminal=True)
|
||||
else:
|
||||
logger.error('Repository contains no records, not importing.', timestamp = False)
|
||||
logger.error('Repository contains no records, not importing.', timestamp = False, terminal=True)
|
||||
except Exception as e:
|
||||
logger.error('Failed to parse block.', error = e)
|
||||
logger.error('Failed to parse block.', error = e, terminal=True)
|
||||
else:
|
||||
logger.error('Block hash not found. Perhaps it has not been synced yet?', timestamp = False)
|
||||
logger.error('Block hash not found. Perhaps it has not been synced yet?', timestamp = False, terminal=True)
|
||||
logger.debug('Is valid hash, but does not belong to a known block.')
|
||||
else:
|
||||
logger.error('Unknown data "%s"; must be block hash.' % str(pkobh), timestamp = False)
|
||||
logger.error('Unknown data "%s"; must be block hash.' % str(pkobh), timestamp = False, terminal=True)
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [block hash]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [block hash]', terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -520,19 +521,19 @@ def commandRemoveRepository():
|
|||
|
||||
blockhash = sys.argv[2]
|
||||
|
||||
if pluginapi.get_utils().validateHash(blockhash):
|
||||
if stringvalidators.validate_hash(blockhash):
|
||||
if blockhash in getRepositories():
|
||||
try:
|
||||
removeRepository(blockhash)
|
||||
logger.info('Successfully removed repository.')
|
||||
logger.info('Successfully removed repository.', terminal=True)
|
||||
except Exception as e:
|
||||
logger.error('Failed to parse block.', error = e)
|
||||
logger.error('Failed to parse block.', error = e, terminal=True)
|
||||
else:
|
||||
logger.error('Repository has not been imported, nothing to remove.', timestamp = False)
|
||||
logger.error('Repository has not been imported, nothing to remove.', timestamp = False, terminal=True)
|
||||
else:
|
||||
logger.error('Unknown data "%s"; must be block hash.' % str(pkobh))
|
||||
logger.error('Unknown data "%s"; must be block hash.' % str(pkobh), terminal=True)
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [block hash]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [block hash]', terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -545,11 +546,11 @@ def commandPublishPlugin():
|
|||
|
||||
if os.path.exists(pluginfolder) and not os.path.isfile(pluginfolder):
|
||||
block = pluginToBlock(pluginname)
|
||||
logger.info('Plugin saved in block %s.' % block)
|
||||
logger.info('Plugin saved in block %s.' % block, terminal=True)
|
||||
else:
|
||||
logger.error('Plugin %s does not exist.' % pluginname, timestamp = False)
|
||||
logger.error('Plugin %s does not exist.' % pluginname, timestamp = False, terminal=True)
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin>')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin>', terminal=True)
|
||||
|
||||
def commandCreateRepository():
|
||||
if len(sys.argv) >= 3:
|
||||
|
@ -573,22 +574,22 @@ def commandCreateRepository():
|
|||
if distributor is None:
|
||||
distributor = getKey(pluginname)
|
||||
if distributor is None:
|
||||
logger.error('No distributor key was found for the plugin %s.' % pluginname, timestamp = False)
|
||||
logger.error('No distributor key was found for the plugin %s.' % pluginname, timestamp = False, terminal=True)
|
||||
success = False
|
||||
|
||||
plugins.append([pluginname, distributor])
|
||||
|
||||
if not success:
|
||||
logger.error('Please correct the above errors, then recreate the repository.')
|
||||
logger.error('Please correct the above errors, then recreate the repository.', terminal=True)
|
||||
return True
|
||||
|
||||
blockhash = createRepository(plugins)
|
||||
if not blockhash is None:
|
||||
logger.info('Successfully created repository. Execute the following command to add the repository:\n ' + logger.colors.underline + '%s --add-repository %s' % (script, blockhash))
|
||||
logger.info('Successfully created repository. Execute the following command to add the repository:\n ' + logger.colors.underline + '%s --add-repository %s' % (script, blockhash), terminal=True)
|
||||
else:
|
||||
logger.error('Failed to create repository, an unknown error occurred.')
|
||||
logger.error('Failed to create repository, an unknown error occurred.', terminal=True)
|
||||
else:
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [plugins...]')
|
||||
logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [plugins...]', terminal=True)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Load the user's inbox and return it as a list
|
||||
'''
|
||||
'''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 onionrblockapi
|
||||
def load_inbox(myCore):
|
||||
inbox_list = []
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
HTTP endpoints for mail plugin.
|
||||
'''
|
||||
|
@ -21,6 +21,7 @@ import sys, os, json
|
|||
from flask import Response, request, redirect, Blueprint, abort
|
||||
import core
|
||||
from onionrusers import contactmanager
|
||||
from onionrutils import stringvalidators
|
||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||
import loadinbox, sentboxdb
|
||||
|
||||
|
@ -34,7 +35,7 @@ def mail_ping():
|
|||
|
||||
@flask_blueprint.route('/mail/deletemsg/<block>', methods=['POST'])
|
||||
def mail_delete(block):
|
||||
if not c._utils.validateHash(block):
|
||||
if not stringvalidators.validate_hash(block):
|
||||
abort(504)
|
||||
existing = kv.get('deleted_mail')
|
||||
if existing is None:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Anonymous Storage Network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This default plugin handles private messages in an email like fashion
|
||||
'''
|
||||
|
@ -23,6 +23,7 @@ import logger, config, threading, time, datetime
|
|||
from onionrblockapi import Block
|
||||
import onionrexceptions
|
||||
from onionrusers import onionrusers
|
||||
from onionrutils import stringvalidators, escapeansi, bytesconverter
|
||||
import locale, sys, os, json
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
@ -73,7 +74,7 @@ class OnionrMail:
|
|||
blockCount = 0
|
||||
pmBlockMap = {}
|
||||
pmBlocks = {}
|
||||
logger.info('Decrypting messages...')
|
||||
logger.info('Decrypting messages...', terminal=True)
|
||||
choice = ''
|
||||
displayList = []
|
||||
subject = ''
|
||||
|
@ -108,7 +109,7 @@ class OnionrMail:
|
|||
displayList.append('%s. %s - %s - <%s>: %s' % (blockCount, blockDate, senderDisplay[:12], subject[:10], blockHash))
|
||||
while choice not in ('-q', 'q', 'quit'):
|
||||
for i in displayList:
|
||||
logger.info(i)
|
||||
logger.info(i, terminal=True)
|
||||
try:
|
||||
choice = logger.readline('Enter a block number, -r to refresh, or -q to stop: ').strip().lower()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
|
@ -135,27 +136,27 @@ class OnionrMail:
|
|||
else:
|
||||
cancel = ''
|
||||
readBlock.verifySig()
|
||||
senderDisplay = self.myCore._utils.bytesToStr(readBlock.signer)
|
||||
senderDisplay = bytesconverter.bytes_to_str(readBlock.signer)
|
||||
if len(senderDisplay.strip()) == 0:
|
||||
senderDisplay = 'Anonymous'
|
||||
logger.info('Message received from %s' % (senderDisplay,))
|
||||
logger.info('Valid signature: %s' % readBlock.validSig)
|
||||
logger.info('Message received from %s' % (senderDisplay,), terminal=True)
|
||||
logger.info('Valid signature: %s' % readBlock.validSig, terminal=True)
|
||||
|
||||
if not readBlock.validSig:
|
||||
logger.warn('This message has an INVALID/NO signature. ANYONE could have sent this message.')
|
||||
logger.warn('This message has an INVALID/NO signature. ANYONE could have sent this message.', terminal=True)
|
||||
cancel = logger.readline('Press enter to continue to message, or -q to not open the message (recommended).')
|
||||
print('')
|
||||
if cancel != '-q':
|
||||
try:
|
||||
print(draw_border(self.myCore._utils.escapeAnsi(readBlock.bcontent.decode().strip())))
|
||||
print(draw_border(escapeansi.escape_ANSI(readBlock.bcontent.decode().strip())))
|
||||
except ValueError:
|
||||
logger.warn('Error presenting message. This is usually due to a malformed or blank message.')
|
||||
logger.warn('Error presenting message. This is usually due to a malformed or blank message.', terminal=True)
|
||||
pass
|
||||
if readBlock.validSig:
|
||||
reply = logger.readline("Press enter to continue, or enter %s to reply" % ("-r",))
|
||||
print('')
|
||||
if reply == "-r":
|
||||
self.draft_message(self.myCore._utils.bytesToStr(readBlock.signer,))
|
||||
self.draft_message(bytesconverter.bytes_to_str(readBlock.signer,))
|
||||
else:
|
||||
logger.readline("Press enter to continue")
|
||||
print('')
|
||||
|
@ -168,7 +169,7 @@ class OnionrMail:
|
|||
entering = True
|
||||
while entering:
|
||||
self.get_sent_list()
|
||||
logger.info('Enter a block number or -q to return')
|
||||
logger.info('Enter a block number or -q to return', terminal=True)
|
||||
try:
|
||||
choice = input('>')
|
||||
except (EOFError, KeyboardInterrupt) as e:
|
||||
|
@ -182,11 +183,11 @@ class OnionrMail:
|
|||
try:
|
||||
self.sentboxList[int(choice)]
|
||||
except (IndexError, ValueError) as e:
|
||||
logger.warn('Invalid block.')
|
||||
logger.warn('Invalid block.', terminal=True)
|
||||
else:
|
||||
logger.info('Sent to: ' + self.sentMessages[self.sentboxList[int(choice)]][1])
|
||||
logger.info('Sent to: ' + self.sentMessages[self.sentboxList[int(choice)]][1], terminal=True)
|
||||
# Print ansi escaped sent message
|
||||
logger.info(self.myCore._utils.escapeAnsi(self.sentMessages[self.sentboxList[int(choice)]][0]))
|
||||
logger.info(escapeansi.escape_ANSI(self.sentMessages[self.sentboxList[int(choice)]][0]), terminal=True)
|
||||
input('Press enter to continue...')
|
||||
finally:
|
||||
if choice == '-q':
|
||||
|
@ -199,9 +200,9 @@ class OnionrMail:
|
|||
self.sentMessages = {}
|
||||
for i in self.sentboxTools.listSent():
|
||||
self.sentboxList.append(i['hash'])
|
||||
self.sentMessages[i['hash']] = (self.myCore._utils.bytesToStr(i['message']), i['peer'], i['subject'])
|
||||
self.sentMessages[i['hash']] = (bytesconverter.bytes_to_str(i['message']), i['peer'], i['subject'])
|
||||
if display:
|
||||
logger.info('%s. %s - %s - (%s) - %s' % (count, i['hash'], i['peer'][:12], i['subject'], i['date']))
|
||||
logger.info('%s. %s - %s - (%s) - %s' % (count, i['hash'], i['peer'][:12], i['subject'], i['date']), terminal=True)
|
||||
count += 1
|
||||
return json.dumps(self.sentMessages)
|
||||
|
||||
|
@ -217,10 +218,10 @@ class OnionrMail:
|
|||
recip = logger.readline('Enter peer address, or -q to stop:').strip()
|
||||
if recip in ('-q', 'q'):
|
||||
raise EOFError
|
||||
if not self.myCore._utils.validatePubKey(recip):
|
||||
if not stringvalidators.validate_pub_key(recip):
|
||||
raise onionrexceptions.InvalidPubkey('Must be a valid ed25519 base32 encoded public key')
|
||||
except onionrexceptions.InvalidPubkey:
|
||||
logger.warn('Invalid public key')
|
||||
logger.warn('Invalid public key', terminal=True)
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
entering = False
|
||||
else:
|
||||
|
@ -234,7 +235,7 @@ class OnionrMail:
|
|||
pass
|
||||
|
||||
cancelEnter = False
|
||||
logger.info('Enter your message, stop by entering -q on a new line. -c to cancel')
|
||||
logger.info('Enter your message, stop by entering -q on a new line. -c to cancel', terminal=True)
|
||||
while newLine != '-q':
|
||||
try:
|
||||
newLine = input()
|
||||
|
@ -249,7 +250,7 @@ class OnionrMail:
|
|||
message += newLine
|
||||
|
||||
if not cancelEnter:
|
||||
logger.info('Inserting encrypted message as Onionr block....')
|
||||
logger.info('Inserting encrypted message as Onionr block....', terminal=True)
|
||||
|
||||
blockID = self.myCore.insertBlock(message, header='pm', encryptType='asym', asymPeer=recip, sign=self.doSigs, meta={'subject': subject})
|
||||
|
||||
|
@ -261,16 +262,16 @@ class OnionrMail:
|
|||
while True:
|
||||
sigMsg = 'Message Signing: %s'
|
||||
|
||||
logger.info(self.strings.programTag + '\n\nUser ID: ' + self.myCore._crypto.pubKey)
|
||||
logger.info(self.strings.programTag + '\n\nUser ID: ' + self.myCore._crypto.pubKey, terminal=True)
|
||||
if self.doSigs:
|
||||
sigMsg = sigMsg % ('enabled',)
|
||||
else:
|
||||
sigMsg = sigMsg % ('disabled (Your messages cannot be trusted)',)
|
||||
if self.doSigs:
|
||||
logger.info(sigMsg)
|
||||
logger.info(sigMsg, terminal=True)
|
||||
else:
|
||||
logger.warn(sigMsg)
|
||||
logger.info(self.strings.mainMenu.title()) # print out main menu
|
||||
logger.warn(sigMsg, terminal=True)
|
||||
logger.info(self.strings.mainMenu.title(), terminal=True) # print out main menu
|
||||
try:
|
||||
choice = logger.readline('Enter 1-%s:\n' % (len(self.strings.mainMenuChoices))).lower().strip()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
|
@ -285,12 +286,12 @@ class OnionrMail:
|
|||
elif choice in (self.strings.mainMenuChoices[3], '4'):
|
||||
self.toggle_signing()
|
||||
elif choice in (self.strings.mainMenuChoices[4], '5'):
|
||||
logger.info('Goodbye.')
|
||||
logger.info('Goodbye.', terminal=True)
|
||||
break
|
||||
elif choice == '':
|
||||
pass
|
||||
else:
|
||||
logger.warn('Invalid choice.')
|
||||
logger.warn('Invalid choice.', terminal=True)
|
||||
return
|
||||
|
||||
def add_deleted(keyStore, bHash):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file handles the sentbox for the mail plugin
|
||||
'''
|
||||
|
@ -19,6 +19,7 @@
|
|||
'''
|
||||
import sqlite3, os
|
||||
import core
|
||||
from onionrutils import epoch
|
||||
class SentBox:
|
||||
def __init__(self, mycore):
|
||||
assert isinstance(mycore, core.Core)
|
||||
|
@ -60,7 +61,7 @@ class SentBox:
|
|||
|
||||
def addToSent(self, blockID, peer, message, subject=''):
|
||||
self.connect()
|
||||
args = (blockID, peer, message, subject, self.core._utils.getEpoch())
|
||||
args = (blockID, peer, message, subject, epoch.get_epoch())
|
||||
self.cursor.execute('INSERT INTO sent VALUES(?, ?, ?, ?, ?)', args)
|
||||
self.conn.commit()
|
||||
self.close()
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
friendList = []
|
||||
friendList = {}
|
||||
convoListElement = document.getElementsByClassName('conversationList')[0]
|
||||
|
||||
function createConvoList(){
|
||||
for (var x = 0; x < friendList.length; x++){
|
||||
console.log(friendList)
|
||||
|
||||
for (friend in friendList){
|
||||
var convoEntry = document.createElement('div')
|
||||
convoEntry.classList.add('convoEntry')
|
||||
convoEntry.setAttribute('data-pubkey', friendList[x])
|
||||
convoEntry.innerText = friendList[x]
|
||||
convoEntry.setAttribute('data-pubkey', friend)
|
||||
convoEntry.innerText = friendList[friend]
|
||||
convoListElement.append(convoEntry)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +22,7 @@ fetch('/friends/list', {
|
|||
var keys = []
|
||||
for(var k in resp) keys.push(k)
|
||||
for (var i = 0; i < keys.length; i++){
|
||||
friendList.push(keys[i])
|
||||
friendList[keys[i]] = resp[keys[i]]['name']
|
||||
}
|
||||
createConvoList()
|
||||
})
|
|
@ -113,4 +113,8 @@ input{
|
|||
color: black;
|
||||
font-size: 1.5em;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.content{
|
||||
min-height: 1000px;
|
||||
}
|
|
@ -58,9 +58,9 @@ function openReply(bHash, quote, subject){
|
|||
// Add quoted reply
|
||||
var splitQuotes = quote.split('\n')
|
||||
for (var x = 0; x < splitQuotes.length; x++){
|
||||
splitQuotes[x] = '>' + splitQuotes[x]
|
||||
splitQuotes[x] = '> ' + splitQuotes[x]
|
||||
}
|
||||
quote = '\n' + splitQuotes.join('\n')
|
||||
quote = '\n' + key.substring(0, 12) + ' wrote:' + '\n' + splitQuotes.join('\n')
|
||||
document.getElementById('draftText').value = quote
|
||||
setActiveTab('send message')
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ function openThread(bHash, sender, date, sigBool, pubkey, subjectLine){
|
|||
var sigMsg = 'signature'
|
||||
|
||||
// show add unknown contact button if peer is unknown but still has pubkey
|
||||
if (sender == pubkey){
|
||||
if (sender === pubkey && sender !== myPub && sigBool){
|
||||
addUnknownContact.style.display = 'inline'
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,11 @@ sendForm.onsubmit = function(){
|
|||
return false
|
||||
}
|
||||
}
|
||||
sendMail(to.value, messageContent.value, subject.value)
|
||||
if (to.value.length !== 56 && to.value.length !== 52){
|
||||
alert('Public key is not valid')
|
||||
}
|
||||
else{
|
||||
sendMail(to.value, messageContent.value, subject.value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -5,4 +5,14 @@
|
|||
|
||||
.saveConfig{
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
|
||||
.idLink{
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome and Opera */
|
||||
}
|
|
@ -18,10 +18,34 @@
|
|||
*/
|
||||
uptimeDisplay = document.getElementById('uptime')
|
||||
connectedDisplay = document.getElementById('connectedNodes')
|
||||
connectedDisplay.style.maxHeight = '300px'
|
||||
connectedDisplay.style.overflowY = 'scroll'
|
||||
storedBlockDisplay = document.getElementById('storedBlocks')
|
||||
queuedBlockDisplay = document.getElementById('blockQueue')
|
||||
lastIncoming = document.getElementById('lastIncoming')
|
||||
totalRec = document.getElementById('totalRec')
|
||||
securityLevel = document.getElementById('securityLevel')
|
||||
sec_description_str = 'unknown'
|
||||
|
||||
function showSecStatNotice(){
|
||||
var secWarnEls = document.getElementsByClassName('secRequestNotice')
|
||||
for (el = 0; el < secWarnEls.length; el++){
|
||||
secWarnEls[el].style.display = 'block'
|
||||
}
|
||||
}
|
||||
|
||||
switch (httpGet('/config/get/general.security_level')){
|
||||
case "0":
|
||||
sec_description_str = 'normal'
|
||||
break;
|
||||
case "1":
|
||||
sec_description_str = 'high'
|
||||
break;
|
||||
}
|
||||
|
||||
if (sec_description_str !== 'normal'){
|
||||
showSecStatNotice()
|
||||
}
|
||||
|
||||
function getStats(){
|
||||
stats = JSON.parse(httpGet('getstats', webpass))
|
||||
|
@ -29,6 +53,7 @@ function getStats(){
|
|||
connectedDisplay.innerText = stats['connectedNodes']
|
||||
storedBlockDisplay.innerText = stats['blockCount']
|
||||
queuedBlockDisplay.innerText = stats['blockQueueCount']
|
||||
securityLevel.innerText = sec_description_str
|
||||
totalRec.innerText = httpGet('/hitcount')
|
||||
var lastConnect = httpGet('/lastconnect')
|
||||
if (lastConnect > 0){
|
||||
|
|
|
@ -178,8 +178,16 @@ body{
|
|||
background-color:#396BAC;
|
||||
}
|
||||
|
||||
.btn:hover{
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.openSiteBtn{
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.hidden{
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import subprocess, os
|
|||
import multiprocessing, threading, time, json
|
||||
from multiprocessing import Pipe, Process
|
||||
import core, onionrblockapi, config, onionrutils, logger, onionrproofs
|
||||
from onionrutils import bytesconverter
|
||||
|
||||
class SubprocessPOW:
|
||||
def __init__(self, data, metadata, core_inst=None, subproc_count=None):
|
||||
|
@ -51,7 +52,7 @@ class SubprocessPOW:
|
|||
# dump dict to measure bytes of json metadata. Cannot reuse later because the pow token must be added
|
||||
json_metadata = json.dumps(metadata).encode()
|
||||
|
||||
self.data = onionrutils.OnionrUtils.strToBytes(data)
|
||||
self.data = bytesconverter.str_to_bytes(data)
|
||||
# Calculate difficulty. Dumb for now, may use good algorithm in the future.
|
||||
self.difficulty = onionrproofs.getDifficultyForNewBlock(bytes(json_metadata + b'\n' + self.data), coreInst=self.core_inst)
|
||||
|
||||
|
@ -111,7 +112,7 @@ class SubprocessPOW:
|
|||
payload = json.dumps(metadata).encode() + b'\n' + data
|
||||
# Check sha3_256 hash of block, compare to puzzle. Send payload if puzzle finished
|
||||
token = mcore._crypto.sha3Hash(payload)
|
||||
token = onionrutils.OnionrUtils.bytesToStr(token) # ensure token is string
|
||||
token = bytesconverter.bytes_to_str(token) # ensure token is string
|
||||
if puzzle == token[0:difficulty]:
|
||||
pipe.send(payload)
|
||||
break
|
||||
|
|
|
@ -4,6 +4,7 @@ sys.path.append(".")
|
|||
import unittest, uuid, hashlib, base64
|
||||
import nacl.exceptions
|
||||
import nacl.signing, nacl.hash, nacl.encoding
|
||||
from onionrutils import stringvalidators, mnemonickeys
|
||||
TEST_DIR = 'testdata/%s-%s' % (uuid.uuid4(), os.path.basename(__file__)) + '/'
|
||||
print("Test directory:", TEST_DIR)
|
||||
os.environ["ONIONR_HOME"] = TEST_DIR
|
||||
|
@ -45,19 +46,12 @@ class OnionrCryptoTests(unittest.TestCase):
|
|||
self.assertEqual(crypto.sha3Hash(b'test'), normal)
|
||||
|
||||
def valid_default_id(self):
|
||||
self.assertTrue(c._utils.validatePubKey(crypto.pubKey))
|
||||
self.assertTrue(stringvalidators.validate_pub_key(crypto.pubKey))
|
||||
|
||||
def test_human_readable_length(self):
|
||||
human = c._utils.getHumanReadableID()
|
||||
human = mnemonickeys.get_human_readable_ID(c)
|
||||
self.assertTrue(len(human.split(' ')) == 32)
|
||||
|
||||
def test_human_readable_rebuild(self):
|
||||
return # Broken right now
|
||||
# Test if we can get the human readable id, and convert it back to valid base32 key
|
||||
human = c._utils.getHumanReadableID()
|
||||
unHuman = c._utils.convertHumanReadableID(human)
|
||||
nacl.signing.VerifyKey(c._utils.convertHumanReadableID(human), encoder=nacl.encoding.Base32Encoder)
|
||||
|
||||
def test_safe_compare(self):
|
||||
self.assertTrue(crypto.safeCompare('test', 'test'))
|
||||
self.assertTrue(crypto.safeCompare('test', b'test'))
|
||||
|
@ -130,7 +124,7 @@ class OnionrCryptoTests(unittest.TestCase):
|
|||
def test_deterministic(self):
|
||||
password = os.urandom(32)
|
||||
gen = crypto.generateDeterministic(password)
|
||||
self.assertTrue(c._utils.validatePubKey(gen[0]))
|
||||
self.assertTrue(stringvalidators.validate_pub_key(gen[0]))
|
||||
try:
|
||||
crypto.generateDeterministic('weakpassword')
|
||||
except onionrexceptions.PasswordStrengthError:
|
||||
|
@ -151,6 +145,6 @@ class OnionrCryptoTests(unittest.TestCase):
|
|||
gen2 = crypto.generateDeterministic(password)
|
||||
self.assertFalse(gen == gen1)
|
||||
self.assertTrue(gen1 == gen2)
|
||||
self.assertTrue(c._utils.validatePubKey(gen1[0]))
|
||||
self.assertTrue(stringvalidators.validate_pub_key(gen1[0]))
|
||||
|
||||
unittest.main()
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue