def blockToPlugin(block): try: block = Block(block) blockContent = json.loads(block.getContent()) name = sanitize(blockContent['name']) author = blockContent['author'] date = blockContent['date'] version = None if 'version' in blockContent['info']: version = blockContent['info']['version'] content = base64.b64decode(blockContent['content'].encode()) source = pluginapi.plugins.get_data_folder(plugin_name) + 'plugin.zip' destination = pluginapi.plugins.get_folder(name) with open(source, 'wb') as f: f.write(content) if os.path.exists(destination) and not os.path.isfile(destination): shutil.rmtree(destination) shutil.unpack_archive(source, destination) pluginapi.plugins.enable(name) logger.info('Installation of %s complete.' % name) return True except Exception as e: logger.error('Failed to install plugin.', error=e, timestamp=False) return False
def commandAddRepository(): if len(sys.argv) >= 3: check() blockhash = sys.argv[2] if pluginapi.get_utils().validateHash(blockhash): if Block.exists(blockhash): try: blockContent = json.loads(Block(blockhash, core = pluginapi.get_core()).getContent()) pluginslist = dict() for pluginname, distributor in blockContent['plugins']: if pluginapi.get_utils().validatePubKey(distributor): pluginslist[pluginname] = distributor logger.debug('Found %s records in repository.' % len(pluginslist)) if len(pluginslist) != 0: addRepository(blockhash, pluginslist) logger.info('Successfully added repository.') else: logger.error('Repository contains no records, not importing.', timestamp = False) except Exception as e: logger.error('Failed to parse block.', error = e) else: 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.') else: logger.error('Unknown data "%s"; must be block hash.' % str(pkobh), timestamp = False) else: logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' [block hash]') return True
def installBlock(block): try: block = Block(block, core = pluginapi.get_core()) blockContent = json.loads(block.getContent()) name = sanitize(blockContent['name']) author = blockContent['author'] date = blockContent['date'] version = None if 'version' in blockContent['info']: version = blockContent['info']['version'] install = False logger.info(('Will install %s' + (' v' + version if not version is None else '') + ' (%s), by %s') % (name, date, author)) # TODO: Convert to single line if statement if os.path.exists(pluginapi.plugins.get_folder(name)): install = logger.confirm(message = 'Continue with installation (will overwrite existing plugin) %s?') else: install = logger.confirm(message = 'Continue with installation %s?') if install: blockToPlugin(block.getHash()) addPlugin(name) else: logger.info('Installation cancelled.') return False return True except Exception as e: logger.error('Failed to install plugin.', error = e, timestamp = False) return False
def getFile(o_inst): ''' Get a file from onionr blocks ''' try: fileName = sys.argv[2] bHash = sys.argv[3] except IndexError: logger.error("Syntax %s %s" % (sys.argv[0], '/path/to/filename <blockhash>')) else: logger.info(fileName) contents = None if os.path.exists(fileName): logger.error("File already exists") return if not o_inst.onionrUtils.validateHash(bHash): logger.error('Block hash is invalid') return with open(fileName, 'wb') as myFile: myFile.write( base64.b64decode( Block(bHash, core=o_inst.onionrCore).bcontent)) return
def showOutput(self): while type(self.channel) is type(None) and self.flowRunning: time.sleep(1) try: while self.flowRunning: 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) 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) self.alreadyOutputed.append(block.getHash()) time.sleep(5) except KeyboardInterrupt: self.flowRunning = False
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
def pluginToBlock(plugin, import_block = True): try: plugin = sanitize(plugin) directory = pluginapi.get_pluginapi().get_folder(plugin) data_directory = pluginapi.get_pluginapi().get_data_folder(plugin) zipfile = pluginapi.get_pluginapi().get_data_folder(plugin_name) + 'plugin.zip' if os.path.exists(directory) and not os.path.isfile(directory): if os.path.exists(data_directory) and not os.path.isfile(data_directory): shutil.rmtree(data_directory) if os.path.exists(zipfile) and os.path.isfile(zipfile): os.remove(zipfile) if os.path.exists(directory + '__pycache__') and not os.path.isfile(directory + '__pycache__'): shutil.rmtree(directory + '__pycache__') shutil.make_archive(zipfile[:-4], 'zip', directory) data = '' with open(zipfile, 'rb') as file: data = base64.b64encode(file.read()) author = getpass.getuser() description = 'Default plugin description' info = {"name" : plugin} try: if os.path.exists(directory + 'info.json'): info = '' with open(directory + 'info.json').read() as file: info = json.loads(file.read()) if 'author' in info: author = info['author'] if 'description' in info: description = info['description'] except: pass metadata = {'author' : author, 'date' : str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')), 'name' : plugin, 'info' : info, 'compiled-by' : plugin_name, 'content' : data.decode('utf-8'), 'description' : description} block = Block(core = pluginapi.get_core()) block.setType('plugin') block.setContent(json.dumps(metadata)) hash = block.save(True) # hash = pluginapi.get_core().insertBlock(, header = 'plugin', sign = True) if import_block: pluginapi.get_utils().importNewBlocks() return hash else: logger.error('Plugin %s does not exist.' % plugin) except Exception as e: logger.error('Failed to convert plugin to block.', error = e, timestamp = False) return False
def createRepository(plugins): contents = {'plugins' : plugins, 'author' : getpass.getuser(), 'compiled-by' : plugin_name} block = Block(core = pluginapi.get_core()) block.setType('repository') block.setContent(json.dumps(contents)) return block.save(True)
def update(): global listedBlocks, listbox, runningCheckDelayCount, runningCheckDelay, root, daemonStatus for i in Block.getBlocks(type='txt'): if i.getContent().strip() == '' or i.getHash() in listedBlocks: continue listbox.insert(99999, str(i.getContent())) listedBlocks.append(i.getHash()) listbox.see(99999) runningCheckDelayCount += 1 if runningCheckDelayCount == runningCheckDelay: resp = pluginapi.daemon.local_command('ping') if resp == 'pong': daemonStatus.config(text="Onionr Daemon Status: Running") else: daemonStatus.config(text="Onionr Daemon Status: Not Running") runningCheckDelayCount = 0 root.after(10000, update)
def commandInstallPlugin(): if len(sys.argv) >= 3: check() pluginname = sys.argv[2] pkobh = None # public key or block hash version = None if ':' in pluginname: details = pluginname pluginname = sanitize(details[0]) version = details[1] sanitize(pluginname) if len(sys.argv) >= 4: # public key or block hash specified pkobh = sys.argv[3] else: # none specified, check if in config file pkobh = getKey(pluginname) if pkobh is None: # still nothing found, try searching repositories logger.info('Searching for public key in repositories...') 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)) distributors.append(records[pluginname]) if len(distributors) != 0: distributor = None if len(distributors) == 1: logger.info('Found distributor: %s' % distributors[0]) distributor = distributors[0] else: distributors_message = '' index = 1 for dist in distributors: 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)) 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)) try: if int(choice) < index and int(choice) >= 1: distributor = distributors[int(choice)] valid = True except KeyboardInterrupt: logger.info('Installation cancelled.') return True except: pass if not distributor is None: pkobh = distributor except Exception as e: logger.warn('Failed to lookup plugin in repositories.', timestamp=False) logger.error('asdf', error=e, timestamp=False) if pkobh is None: logger.error( 'No key for this plugin found in keystore or repositories, please specify.' ) help() return True valid_hash = pluginapi.get_utils().validateHash(pkobh) real_block = False valid_key = pluginapi.get_utils().validatePubKey(pkobh) real_key = False if valid_hash: real_block = Block.exists(pkobh) elif valid_key: real_key = pluginapi.get_utils().hasKey(pkobh) blockhash = None if valid_hash and not real_block: logger.error( 'Block hash not found. Perhaps it has not been synced yet?') logger.debug( 'Is valid hash, but does not belong to a known block.') return True elif valid_hash and real_block: blockhash = str(pkobh) logger.debug('Using block %s...' % blockhash) installBlock(blockhash) elif valid_key and not real_key: logger.error( 'Public key not found. Try adding the node by address manually, if possible.' ) logger.debug('Is valid key, but the key is not a known one.') elif valid_key and real_key: publickey = str(pkobh) logger.debug('Using public key %s...' % publickey) saveKey(pluginname, pkobh) signedBlocks = Block.getBlocks(type='plugin', signed=True, signer=publickey) mostRecentTimestamp = None mostRecentVersionBlock = None for block in signedBlocks: try: blockContent = json.loads(block.getContent()) if not (('author' in blockContent) and ('info' in blockContent) and ('date' in blockContent) and ('name' in blockContent)): raise ValueError( 'Missing required parameter `date` in block %s.' % block.getHash()) blockDatetime = datetime.datetime.strptime( blockContent['date'], '%Y-%m-%d %H:%M:%S') if blockContent['name'] == pluginname: if ('version' in blockContent['info']) and ( blockContent['info']['version'] == version) and (not version is None): mostRecentTimestamp = blockDatetime mostRecentVersionBlock = block.getHash() break elif mostRecentTimestamp is None: mostRecentTimestamp = blockDatetime mostRecentVersionBlock = block.getHash() elif blockDatetime > mostRecentTimestamp: mostRecentTimestamp = blockDatetime mostRecentVersionBlock = block.getHash() except Exception as e: pass logger.warn( 'Only continue the installation is you are absolutely certain that you trust the plugin distributor. Public key of plugin distributor: %s' % publickey, timestamp=False) installBlock(mostRecentVersionBlock) else: logger.error( 'Unknown data "%s"; must be public key or block hash.' % str(pkobh)) return else: logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' <plugin> [public key/block hash]') return True
def send(): global message block = Block() block.setType('txt') block.setContent(message) logger.debug('Sent message in block %s.' % block.save(sign=True))
def inbox(self): blockCount = 0 pmBlockMap = {} pmBlocks = {} logger.info('Decrypting messages...') choice = '' displayList = [] subject = '' # this could use a lot of memory if someone has received a lot of messages for blockHash in self.myCore.getBlocksByType('pm'): pmBlocks[blockHash] = Block(blockHash, core=self.myCore) pmBlocks[blockHash].decrypt() blockCount = 0 for blockHash in pmBlocks: if not pmBlocks[blockHash].decrypted: continue blockCount += 1 pmBlockMap[blockCount] = blockHash block = pmBlocks[blockHash] senderKey = block.signer try: senderKey = senderKey.decode() except AttributeError: pass senderDisplay = onionrusers.OnionrUser(self.myCore, senderKey).getName() if senderDisplay == 'anonymous': senderDisplay = senderKey blockDate = pmBlocks[blockHash].getDate().strftime("%m/%d %H:%M") try: subject = pmBlocks[blockHash].bmetadata['subject'] except KeyError: subject = '' 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) try: choice = logger.readline( 'Enter a block number, -r to refresh, or -q to stop: ' ).strip().lower() except (EOFError, KeyboardInterrupt): choice = '-q' if choice in ('-q', 'q', 'quit'): continue if choice in ('-r', 'r', 'refresh'): # dirty hack self.inbox() return try: choice = int(choice) except ValueError: pass else: try: pmBlockMap[choice] readBlock = pmBlocks[pmBlockMap[choice]] except KeyError: pass else: cancel = '' readBlock.verifySig() senderDisplay = self.myCore._utils.bytesToStr( readBlock.signer) if len(senderDisplay.strip()) == 0: senderDisplay = 'Anonymous' logger.info('Message received from %s' % (senderDisplay, )) logger.info('Valid signature: %s' % readBlock.validSig) if not readBlock.validSig: logger.warn( 'This message has an INVALID/NO signature. ANYONE could have sent this message.' ) 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()))) except ValueError: logger.warn( 'Error presenting message. This is usually due to a malformed or blank message.' ) 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, )) else: logger.readline("Press enter to continue") print('') return
def show_stats(o_inst): try: # define stats messages here totalBlocks = len(o_inst.onionrCore.getBlockList()) 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'), # 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')), # count stats 'div2': True, 'Known Peers': str(max(len(o_inst.onionrCore.listPeers()) - 1, 0)), 'Enabled Plugins': str(len(o_inst.onionrCore.config.get('plugins.enabled', list()))) + ' / ' + str(len(os.listdir(o_inst.dataDir + 'plugins/'))), 'Stored Blocks': str(totalBlocks), 'Percent Blocks Signed': str(round(100 * signedBlocks / max(totalBlocks, 1), 2)) + '%' } # color configuration colors = { 'title': logger.colors.bold, 'key': logger.colors.fg.lightgreen, 'val': logger.colors.fg.green, 'border': logger.colors.fg.lightblue, 'reset': logger.colors.reset } # pre-processing maxlength = 0 width = o_inst.getConsoleWidth() for key, val in messages.items(): if not (type(val) is bool and val is True): maxlength = max(len(key), maxlength) prewidth = maxlength + len(' | ') 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']) 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']) for value in val: logger.info(' ' * maxlength + colors['border'] + ' | ' + colors['reset'] + colors['val'] + str(value) + colors['reset']) else: logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset']) logger.info(colors['border'] + '-' * (maxlength + 1) + '+' + colors['reset']) except Exception as e: logger.error('Failed to generate statistics table.', error=e, timestamp=False)