From bcc475dc61b9cadc179a677c355cad94a6d36570 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Wed, 3 Jul 2019 21:28:16 -0500 Subject: [PATCH] refactor downloadblocks, removed old main.js, correct replay protection check --- onionr/communicatorutils/downloadblocks.py | 35 +- onionr/httpapi/miscpublicapi/endpoints.py | 1 + onionr/onionrblockapi.py | 2 +- onionr/static-data/www/ui/dist/js/main.js | 753 --------------------- 4 files changed, 24 insertions(+), 767 deletions(-) delete mode 100755 onionr/static-data/www/ui/dist/js/main.js diff --git a/onionr/communicatorutils/downloadblocks.py b/onionr/communicatorutils/downloadblocks.py index 76ad178f..a1ab18c9 100755 --- a/onionr/communicatorutils/downloadblocks.py +++ b/onionr/communicatorutils/downloadblocks.py @@ -21,6 +21,21 @@ import communicator, onionrexceptions import logger, onionrpeers from onionrutils import blockmetadata, stringvalidators, validatemetadata +def _should_download(comm_inst, block_hash): + ret_data = True + if block_hash in comm_inst._core.getBlockList(): + #logger.debug('Block %s is already saved.' % (blockHash,)) + ret_data = False + else: + if comm_inst._core._blacklist.inBlacklist(blockHash): + ret_data = False + if ret_data is False: + try: + del comm_inst.blockQueue[blockHash] + except KeyError: + pass + return ret_data + def download_blocks_from_communicator(comm_inst): assert isinstance(comm_inst, communicator.OnionrCommunicatorDaemon) for blockHash in list(comm_inst.blockQueue): @@ -32,24 +47,18 @@ def download_blocks_from_communicator(comm_inst): except KeyError: blockPeers = [] removeFromQueue = True - if comm_inst.shutdown or not comm_inst.isOnline: - # Exit loop if shutting down or offline + + if _should_download(comm_inst, blockHash): + continue + + if comm_inst.shutdown or not comm_inst.isOnline or comm_inst._core.storage_counter.isFull(): + # Exit loop if shutting down or offline, or disk allocation reached break # Do not download blocks being downloaded or that are already saved (edge cases) if blockHash in comm_inst.currentDownloading: #logger.debug('Already downloading block %s...' % blockHash) continue - if blockHash in comm_inst._core.getBlockList(): - #logger.debug('Block %s is already saved.' % (blockHash,)) - try: - del comm_inst.blockQueue[blockHash] - except KeyError: - pass - continue - if comm_inst._core._blacklist.inBlacklist(blockHash): - continue - 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: peerUsed = comm_inst.pickOnlinePeer() diff --git a/onionr/httpapi/miscpublicapi/endpoints.py b/onionr/httpapi/miscpublicapi/endpoints.py index d76b2949..05e824a6 100644 --- a/onionr/httpapi/miscpublicapi/endpoints.py +++ b/onionr/httpapi/miscpublicapi/endpoints.py @@ -68,6 +68,7 @@ class PublicEndpoints: @public_endpoints_bp.route('/announce', methods=['post']) def accept_announce(): + '''Accept announcements with pow token to prevent spam''' resp = announce.handle_announce(client_API, request) return resp diff --git a/onionr/onionrblockapi.py b/onionr/onionrblockapi.py index 8464659f..00d377b0 100755 --- a/onionr/onionrblockapi.py +++ b/onionr/onionrblockapi.py @@ -89,7 +89,7 @@ class Block: # Check for replay attacks try: - if epoch.get_epoch() - 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: diff --git a/onionr/static-data/www/ui/dist/js/main.js b/onionr/static-data/www/ui/dist/js/main.js deleted file mode 100755 index 0ddf141e..00000000 --- a/onionr/static-data/www/ui/dist/js/main.js +++ /dev/null @@ -1,753 +0,0 @@ - -/* handy localstorage functions for quick usage */ - -function set(key, val) { - return localStorage.setItem(key, val); -} - -function get(key, df) { // df is default - value = localStorage.getItem(key); - if(value == null) - value = df; - - return value; -} - -function remove(key) { - return localStorage.removeItem(key); -} - -function getParameter(name) { - var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); - return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); -} - -/* usermap localStorage stuff */ - -var usermap = JSON.parse(get('usermap', '{}')); -var postmap = JSON.parse(get('postmap', '{}')) - -function getUserMap() { - return usermap; -} - -function getPostMap(hash) { - if(hash !== undefined) { - if(hash in postmap) - return postmap[hash]; - return null; - } - - return postmap; -} - -function deserializeUser(id) { - if(!(id in getUserMap())) - return null; - - var serialized = getUserMap()[id] - var user = new User(); - - user.setName(serialized['name']); - user.setID(serialized['id']); - user.setIcon(serialized['icon']); - user.setDescription(serialized['description']); - - return user; -} - -function getCurrentUser() { - var user = get('currentUser', null); - - if(user === null) - return null; - - return User.getUser(user, function() {}); -} - -function setCurrentUser(user) { - set('currentUser', user.getID()); -} - -/* returns a relative date format, e.g. "5 minutes" */ -function timeSince(date, size) { - // taken from https://stackoverflow.com/a/3177838/3678023 - - var seconds = Math.floor((new Date() - date) / 1000); - var interval = Math.floor(seconds / 31536000); - - if (size === null) - size = 'desktop'; - - var dates = { - 'mobile' : { - 'yr' : 'yrs', - 'mo' : 'mo', - 'd' : 'd', - 'hr' : 'h', - 'min' : 'm', - 'secs' : 's', - 'sec' : 's', - }, - - 'desktop' : { - 'yr' : ' years', - 'mo' : ' months', - 'd' : ' days', - 'hr' : ' hours', - 'min' : ' minutes', - 'secs' : ' seconds', - 'sec' : ' second', - }, - }; - - if (interval > 1) - return interval + dates[size]['yr']; - interval = Math.floor(seconds / 2592000); - - if (interval > 1) - return interval + dates[size]['mo']; - interval = Math.floor(seconds / 86400); - - if (interval > 1) - return interval + dates[size]['d']; - interval = Math.floor(seconds / 3600); - - if (interval > 1) - return interval + dates[size]['hr']; - interval = Math.floor(seconds / 60); - - if (interval > 1) - return interval + dates[size]['min']; - - if(Math.floor(seconds) !== 1) - return Math.floor(seconds) + dates[size]['secs']; - - return '1' + dates[size]['sec']; -} - -/* replace all instances of string */ -String.prototype.replaceAll = function(search, replacement, limit) { - // taken from https://stackoverflow.com/a/17606289/3678023 - var target = this; - return target.split(search, limit).join(replacement); -}; - -/* useful functions to sanitize data */ -class Sanitize { - /* sanitizes HTML in a string */ - static html(html) { - return String(html).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); - } - - /* URL encodes a string */ - static url(url) { - return encodeURIComponent(url); - } - - /* usernames */ - static username(username) { - return String(username).replace(/[\W_]+/g, " ").substring(0, 25); - } - - /* profile descriptions */ - static description(description) { - return String(description).substring(0, 128); - } -} - -/* config stuff */ -function getWebPassword() { - return get("web-password", null); -} - -function setWebPassword(password) { - return set("web-password", password); -} - -function getTimingToken() { - return get("timing-token", null); -} - -function setTimingToken(token) { - return set("timing-token", token); -} - -/* user class */ -class User { - constructor() { - this.name = 'Unknown'; - this.id = 'unknown'; - this.image = 'img/default.png'; - } - - setName(name) { - this.name = name; - } - - getName() { - return this.name; - } - - setID(id) { - this.id = id; - } - - getID() { - return this.id; - } - - setIcon(image) { - this.image = image; - } - - getIcon() { - return this.image; - } - - setDescription(description) { - this.description = description; - } - - getDescription() { - return this.description; - } - - serialize() { - return { - 'name' : this.getName(), - 'id' : this.getID(), - 'icon' : this.getIcon(), - 'description' : this.getDescription() - }; - } - - /* save in usermap */ - remember() { - usermap[this.getID()] = this.serialize(); - set('usermap', JSON.stringify(usermap)); - } - - /* save as a block */ - save(callback) { - var block = new Block(); - - block.setType('onionr-user'); - block.setContent(JSON.stringify(this.serialize())); - - return block.save(true, callback); - } - - static getUser(id, callback) { - // console.log(callback); - var user = deserializeUser(id); - if(user === null) { - Block.getBlocks({'type' : 'onionr-user-info', 'signed' : true, 'reverse' : true}, function(data) { - if(data.length !== 0) { - try { - user = new User(); - - var userInfo = JSON.parse(data[0].getContent()); - - if(userInfo['id'] === id) { - user.setName(userInfo['name']); - user.setIcon(userInfo['icon']); - user.setDescription(userInfo['description']); - user.setID(id); - - user.remember(); - // console.log(callback); - callback(user); - return user; - } - } catch(e) { - console.log(e); - - callback(null); - return null; - } - } else { - callback(null); - return null; - } - }); - } else { - // console.log(callback); - callback(user); - return user; - } - } -} - -/* post class */ -class Post { - /* returns the html content of a post */ - getHTML(type) { - var replyTemplate = '\ -
\ -
\ -
\ -
\ - \ -
\ -
\ -
\ -
\ - $user-name\ -
\ -\ -
\ - \ -
\ -
\ -\ -
\ - $content\ -
\ -\ -
\ - $liked\ - reply\ -
\ -
\ -
\ -
\ -
\ -\ -'; - var postTemplate = '\ -
\ -
\ -
\ -
\ - \ -
\ -
\ -
\ - \ -\ -
\ - \ -
\ -
\ -\ -
\ - $content\ -
\ -\ -
\ - $liked\ - reply\ -
\ -
\ -
\ -
\ -
\ -\ -'; - - var template = ''; - - if(type !== undefined && type !== null && type == 'reply') - template = replyTemplate; - else - template = postTemplate; - - var device = (jQuery(document).width() < 768 ? 'mobile' : 'desktop'); - - template = template.replaceAll('$user-name-url', Sanitize.html(Sanitize.url(this.getUser().getName()))); - template = template.replaceAll('$user-name', Sanitize.html(this.getUser().getName())); - template = template.replaceAll('$user-id-url', Sanitize.html(Sanitize.url(this.getUser().getID()))); - - template = template.replaceAll('$user-id-truncated', Sanitize.html(this.getUser().getID().substring(0, 12) + '...')); - // template = template.replaceAll('$user-id-truncated', Sanitize.html(this.getUser().getID().split('-').slice(0, 4).join('-'))); - - template = template.replaceAll('$user-id', Sanitize.html(this.getUser().getID())); - template = template.replaceAll('$user-image', "data:image/jpeg;base64," + Sanitize.html(this.getUser().getIcon())); - template = template.replaceAll('$content', Sanitize.html(this.getContent()).replaceAll('\n', '
', 16)); // Maximum of 16 lines - template = template.replaceAll('$post-hash', this.getHash()); - template = template.replaceAll('$date-relative-truncated', timeSince(this.getPostDate(), 'mobile')); - template = template.replaceAll('$date-relative', timeSince(this.getPostDate(), device) + (device === 'desktop' ? ' ago' : '')); - template = template.replaceAll('$date', this.getPostDate().toLocaleString()); - - if(this.getHash() in getPostMap() && getPostMap()[this.getHash()]['liked']) { - template = template.replaceAll('$liked', 'unlike'); - } else { - template = template.replaceAll('$liked', 'like'); - } - - return template; - } - - setUser(user) { - this.user = user; - } - - getUser() { - return this.user; - } - - setContent(content) { - this.content = content; - } - - getContent() { - return this.content; - } - - setParent(parent) { - this.parent = parent; - } - - getParent() { - return this.parent; - } - - setPostDate(date) { // unix timestamp input - if(date instanceof Date) - this.date = date; - else - this.date = new Date(date * 1000); - } - - getPostDate() { - return this.date; - } - - setHash(hash) { - this.hash = hash; - } - - getHash() { - return this.hash; - } - - save(callback) { - var args = {'type' : 'onionr-post', 'sign' : true, 'content' : JSON.stringify({'content' : this.getContent()})}; - - if(this.getParent() !== undefined && this.getParent() !== null) - args['parent'] = (this.getParent() instanceof Post ? this.getParent().getHash() : (this.getParent() instanceof Block ? this.getParent().getHash() : this.getParent())); - - var url = '/client/?action=insertBlock&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken()); - - console.log(url); - - var http = new XMLHttpRequest(); - - if(callback !== undefined) { - // async - - var thisObject = this; - - http.addEventListener('load', function() { - thisObject.setHash(Block.parseBlockArray(JSON.parse(http.responseText)['hash'])); - callback(thisObject.getHash()); - }, false); - - http.open('GET', url, true); - http.timeout = 5000; - http.send(null); - } else { - // sync - - http.open('GET', url, false); - http.send(null); - - this.setHash(Block.parseBlockArray(JSON.parse(http.responseText)['hash'])); - - return this.getHash(); - } - } -} - -/* block class */ -class Block { - constructor(type, content) { - this.type = type; - this.content = content; - } - - // returns the block hash, if any - getHash() { - return this.hash; - } - - // returns the block type - getType() { - return this.type; - } - - // returns the block header - getHeader(key, df) { // df is default - if(key !== undefined) { - if(this.getHeader().hasOwnProperty(key)) - return this.getHeader()[key]; - else - return (df === undefined ? null : df); - } else - return this.header; - } - - // returns the block metadata - getMetadata(key, df) { // df is default - if(key !== undefined) { - if(this.getMetadata().hasOwnProperty(key)) - return this.getMetadata()[key]; - else - return (df === undefined ? null : df); - } else - return this.metadata; - } - - // returns the block content - getContent() { - return this.content; - } - - // returns the parent block's hash (not Block object, for performance) - getParent() { - // console.log(this.parent); - - // TODO: Create a function to fetch the block contents and parse it from the server; right now it is only possible to search for types of blocks (see Block.getBlocks), so it is impossible to return a Block object here - - // if(!(this.parent instanceof Block) && this.parent !== undefined && this.parent !== null) - // this.parent = Block.openBlock(this.parent); // convert hash to Block object - return this.parent; - } - - // returns the date that the block was received - getDate() { - return this.date; - } - - // returns a boolean that indicates whether or not the block is valid - isValid() { - return this.valid; - } - - // returns a boolean thati ndicates whether or not the block is signed - isSigned() { - return this.signed; - } - - // returns the block signature - getSignature() { - return this.signature; - } - - // returns the block type - setType(type) { - this.type = type; - return this; - } - - // sets block metadata by key - setMetadata(key, val) { - this.metadata[key] = val; - return this; - } - - // sets block content - setContent(content) { - this.content = content; - return this; - } - - // sets the block parent by hash or Block object - setParent(parent) { - this.parent = parent; - return this; - } - - // indicates if the Block exists or not - exists() { - return !(this.hash === null || this.hash === undefined); - } - - // saves the block, returns the hash - save(sign, callback) { - var type = this.getType(); - var content = this.getContent(); - var parent = this.getParent(); - - if(content !== undefined && content !== null && type !== '') { - var args = {'content' : content}; - - if(type !== undefined && type !== null && type !== '') - args['type'] = type; - if(parent !== undefined && parent !== null && parent.getHash() !== undefined && parent.getHash() !== null && parent.getHash() !== '') - args['parent'] = parent.getHash(); - if(sign !== undefined && sign !== null) - args['sign'] = String(sign) !== 'false' - - - var url = '/client/?action=insertBlock&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken()); - - console.log(url); - - var http = new XMLHttpRequest(); - - if(callback !== undefined) { - // async - - http.addEventListener('load', function() { - callback(Block.parseBlockArray(JSON.parse(http.responseText)['hash'])); - }, false); - - http.open('GET', url, true); - http.timeout = 5000; - http.send(null); - } else { - // sync - - http.open('GET', url, false); - http.send(null); - - return Block.parseBlockArray(JSON.parse(http.responseText)['hash']); - } - } - - return false; - } - - /* static functions */ - - // recreates a block by hash - static openBlock(hash) { - return Block.parseBlock(hash); - } - - // converts an associative array to a Block - static parseBlock(val) { - var block = new Block(); - - block.type = val['type']; - block.content = val['content']; - block.header = val['header']; - block.metadata = val['metadata']; - block.date = new Date(val['date'] * 1000); - block.hash = val['hash']; - block.signature = val['signature']; - block.signed = val['signed']; - block.valid = val['valid']; - block.parent = val['parent']; - - if(block.getParent() !== null) { - // if the block data is already in the associative array - - /* - if (blocks.hasOwnProperty(block.getParent())) - block.setParent(Block.parseAssociativeArray({blocks[block.getParent()]})[0]); - */ - } - - return block; - } - - // converts an array of associative arrays to an array of Blocks - static parseBlockArray(blocks) { - var outputBlocks = []; - - for(var key in blocks) { - if(blocks.hasOwnProperty(key)) { - var val = blocks[key]; - - var block = Block.parseBlock(val); - - outputBlocks.push(block); - } - } - - return outputBlocks; - } - - static getBlocks(args, callback) { // callback is optional - args = args || {} - - var url = '/client/?action=searchBlocks&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken()); - - console.log(url); - - var http = new XMLHttpRequest(); - - if(callback !== undefined) { - // async - - http.addEventListener('load', function() { - callback(Block.parseBlockArray(JSON.parse(http.responseText)['blocks'])); - }, false); - - http.open('GET', url, true); - http.timeout = 5000; - http.send(null); - } else { - // sync - - http.open('GET', url, false); - http.send(null); - - return Block.parseBlockArray(JSON.parse(http.responseText)['blocks']); - } - } -} - -/* temporary code */ - -var tt = getParameter("timingToken"); -if(tt !== null && tt !== undefined) { - setTimingToken(tt); -} - -if(getWebPassword() === null) { - var password = ""; - while(password.length != 64) { - password = prompt("Please enter the web password (run `./RUN-LINUX.sh --details`)"); - } - - setWebPassword(password); -} - -if(getCurrentUser() === null) { - jQuery('#modal').modal('show'); - - var url = '/client/?action=info&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken()); - - console.log(url); - - var http = new XMLHttpRequest(); - - // sync - - http.addEventListener('load', function() { - var id = JSON.parse(http.responseText)['pubkey']; - - User.getUser(id, function(data) { - if(data === null || data === undefined) { - var user = new User(); - - user.setName('New User'); - user.setID(id); - user.setIcon('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\ -'); - user.setDescription('A new OnionrUI user'); - - user.remember(); - user.save(); - - setCurrentUser(user); - } else { - setCurrentUser(data); - } - - window.location.reload(); - }); - }, false); - - http.open('GET', url, true); - http.send(null); -} - -currentUser = getCurrentUser();