2017-12-26 07:25:29 +00:00
#!/usr/bin/env python3
'''
2018-08-27 03:44:32 +00:00
Onionr - P2P Anonymous Storage Network
2018-01-14 00:07:13 +00:00
Onionr is the name for both the protocol and the original / reference software .
Run with ' help ' for usage .
'''
'''
2017-12-26 07:25:29 +00:00
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 / > .
'''
2018-04-21 05:04:03 +00:00
2018-05-01 07:25:31 +00:00
import sys
if sys . version_info [ 0 ] == 2 or sys . version_info [ 1 ] < 5 :
2018-05-03 01:14:00 +00:00
print ( ' Error, Onionr requires Python 3.4+ ' )
2018-05-01 07:25:31 +00:00
sys . exit ( 1 )
2018-07-09 07:02:33 +00:00
import os , base64 , random , getpass , shutil , subprocess , requests , time , platform , datetime , re , json , getpass , sqlite3
2018-09-20 05:13:26 +00:00
import webbrowser
2018-04-22 00:09:48 +00:00
from threading import Thread
2018-04-21 03:10:50 +00:00
import api , core , config , logger , onionrplugins as plugins , onionrevents as events
2018-05-11 02:05:56 +00:00
import onionrutils
2018-01-09 22:58:12 +00:00
from onionrutils import OnionrUtils
2018-01-19 09:16:38 +00:00
from netcontroller import NetController
2018-06-01 04:25:28 +00:00
from onionrblockapi import Block
2018-08-27 03:44:32 +00:00
import onionrproofs , onionrexceptions , onionrusers
2017-12-28 19:30:15 +00:00
2018-01-28 01:53:24 +00:00
try :
from urllib3 . contrib . socks import SOCKSProxyManager
except ImportError :
raise Exception ( " You need the PySocks module (for use with socks5 proxy to use Tor) " )
2018-04-19 01:17:47 +00:00
ONIONR_TAGLINE = ' Anonymous P2P Platform - GPLv3 - https://Onionr.VoidNet.Tech '
2018-08-08 19:26:02 +00:00
ONIONR_VERSION = ' 0.2.0 ' # for debugging and stuff
2018-05-19 22:27:26 +00:00
ONIONR_VERSION_TUPLE = tuple ( ONIONR_VERSION . split ( ' . ' ) ) # (MAJOR, MINOR, VERSION)
2018-07-31 04:41:32 +00:00
API_VERSION = ' 4 ' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes know how to communicate without learning too much information about you.
2018-02-04 01:11:35 +00:00
2017-12-26 07:25:29 +00:00
class Onionr :
def __init__ ( self ) :
2018-02-04 01:11:35 +00:00
'''
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 .
2018-01-14 00:07:13 +00:00
'''
2018-09-04 18:56:05 +00:00
self . userRunDir = os . getcwd ( ) # Directory user runs the program from
2018-01-15 08:52:45 +00:00
try :
os . chdir ( sys . path [ 0 ] )
except FileNotFoundError :
pass
2018-02-04 01:11:35 +00:00
2018-02-23 01:58:36 +00:00
# Load global configuration data
2018-04-21 05:04:03 +00:00
data_exists = os . path . exists ( ' data/ ' )
if not data_exists :
os . mkdir ( ' data/ ' )
2018-05-04 07:14:27 +00:00
if os . path . exists ( ' static-data/default_config.json ' ) :
config . set_config ( json . loads ( open ( ' static-data/default_config.json ' ) . read ( ) ) ) # this is the default config, it will be overwritten if a config file already exists. Else, it saves it
else :
# the default config file doesn't exist, try hardcoded config
2018-06-14 04:17:58 +00:00
config . set_config ( { ' dev_mode ' : True , ' log ' : { ' file ' : { ' output ' : True , ' path ' : ' data/output.log ' } , ' console ' : { ' output ' : True , ' color ' : True } } } )
2018-05-04 17:44:35 +00:00
if not data_exists :
2018-04-21 05:04:03 +00:00
config . save ( )
2018-02-23 01:58:36 +00:00
config . reload ( ) # this will read the configuration file into memory
2018-02-23 02:53:49 +00:00
settings = 0b000
2018-06-14 04:17:58 +00:00
if config . get ( ' log.console.color ' , True ) :
2018-02-23 02:53:49 +00:00
settings = settings | logger . USE_ANSI
2018-06-14 04:17:58 +00:00
if config . get ( ' log.console.output ' , True ) :
2018-02-23 02:53:49 +00:00
settings = settings | logger . OUTPUT_TO_CONSOLE
2018-06-14 04:17:58 +00:00
if config . get ( ' log.file.output ' , True ) :
2018-02-23 02:53:49 +00:00
settings = settings | logger . OUTPUT_TO_FILE
2018-06-14 04:17:58 +00:00
logger . set_file ( config . get ( ' log.file.path ' , ' /tmp/onionr.log ' ) )
2018-02-23 02:53:49 +00:00
logger . set_settings ( settings )
2018-06-14 04:17:58 +00:00
if str ( config . get ( ' general.dev_mode ' , True ) ) . lower ( ) == ' true ' :
2018-01-10 03:50:38 +00:00
self . _developmentMode = True
2018-01-26 07:22:48 +00:00
logger . set_level ( logger . LEVEL_DEBUG )
2018-01-10 03:50:38 +00:00
else :
self . _developmentMode = False
2018-01-26 07:22:48 +00:00
logger . set_level ( logger . LEVEL_INFO )
2017-12-27 01:13:19 +00:00
2018-01-14 08:48:23 +00:00
self . onionrCore = core . Core ( )
2018-01-26 06:28:11 +00:00
self . onionrUtils = OnionrUtils ( self . onionrCore )
2018-01-09 22:58:12 +00:00
2018-02-23 01:58:36 +00:00
# Handle commands
2018-01-26 07:22:48 +00:00
2018-01-02 08:43:29 +00:00
self . debug = False # Whole application debugging
2017-12-27 01:13:19 +00:00
2018-01-09 22:58:12 +00:00
if os . path . exists ( ' data-encrypted.dat ' ) :
while True :
print ( ' Enter password to decrypt: ' )
password = getpass . getpass ( )
2018-01-14 08:48:23 +00:00
result = self . onionrCore . dataDirDecrypt ( password )
2018-01-09 22:58:12 +00:00
if os . path . exists ( ' data/ ' ) :
break
else :
2018-04-19 02:25:16 +00:00
logger . error ( ' Failed to decrypt: ' + result [ 1 ] , timestamp = False )
2018-01-09 22:58:12 +00:00
else :
2018-04-21 05:04:03 +00:00
# If data folder does not exist
if not data_exists :
if not os . path . exists ( ' data/blocks/ ' ) :
os . mkdir ( ' data/blocks/ ' )
2018-05-13 03:45:32 +00:00
# Copy default plugins into plugins folder
2018-04-23 03:42:37 +00:00
if not os . path . exists ( plugins . get_plugins_folder ( ) ) :
2018-05-06 23:00:28 +00:00
if os . path . exists ( ' static-data/default-plugins/ ' ) :
names = [ f for f in os . listdir ( " static-data/default-plugins/ " ) if not os . path . isfile ( f ) ]
shutil . copytree ( ' static-data/default-plugins/ ' , plugins . get_plugins_folder ( ) )
2018-04-21 05:04:03 +00:00
# Enable plugins
for name in names :
2018-04-22 00:37:20 +00:00
if not name in plugins . get_enabled_plugins ( ) :
plugins . enable ( name , self )
2018-01-26 07:22:48 +00:00
2018-04-23 03:42:37 +00:00
for name in plugins . get_enabled_plugins ( ) :
if not os . path . exists ( plugins . get_plugin_data_folder ( name ) ) :
2018-04-23 04:25:54 +00:00
try :
os . mkdir ( plugins . get_plugin_data_folder ( name ) )
except :
plugins . disable ( name , onionr = self , stop_event = False )
2018-04-23 03:42:37 +00:00
2018-02-22 09:33:30 +00:00
if not os . path . exists ( self . onionrCore . peerDB ) :
2018-01-14 08:48:23 +00:00
self . onionrCore . createPeerDB ( )
2018-01-10 03:50:38 +00:00
pass
2018-02-22 09:33:30 +00:00
if not os . path . exists ( self . onionrCore . addressDB ) :
self . onionrCore . createAddressDB ( )
2017-12-28 00:18:00 +00:00
2017-12-27 05:00:02 +00:00
# Get configuration
2018-08-05 06:35:49 +00:00
if type ( config . get ( ' client.hmac ' ) ) is type ( None ) :
config . set ( ' client.hmac ' , base64 . b16encode ( os . urandom ( 32 ) ) . decode ( ' utf-8 ' ) , savefile = True )
if type ( config . get ( ' client.port ' ) ) is type ( None ) :
2018-08-06 07:50:08 +00:00
randomPort = 0
while randomPort < 1024 :
randomPort = self . onionrCore . _crypto . secrets . randbelow ( 65535 )
2018-08-05 06:35:49 +00:00
config . set ( ' client.port ' , randomPort , savefile = True )
if type ( config . get ( ' client.participate ' ) ) is type ( None ) :
config . set ( ' client.participate ' , True , savefile = True )
if type ( config . get ( ' client.api_version ' ) ) is type ( None ) :
config . set ( ' client.api_version ' , API_VERSION , savefile = True )
2018-02-04 01:11:35 +00:00
2018-03-03 07:00:43 +00:00
self . cmds = {
2018-03-03 04:19:01 +00:00
' ' : self . showHelpSuggestion ,
2018-02-23 02:25:05 +00:00
' help ' : self . showHelp ,
' version ' : self . version ,
' config ' : self . configure ,
2018-02-04 01:11:35 +00:00
' start ' : self . start ,
' stop ' : self . killDaemon ,
2018-05-11 03:19:48 +00:00
' status ' : self . showStats ,
' statistics ' : self . showStats ,
2018-02-23 02:25:05 +00:00
' stats ' : self . showStats ,
2018-03-03 04:19:01 +00:00
' enable-plugin ' : self . enablePlugin ,
' enplugin ' : self . enablePlugin ,
' enableplugin ' : self . enablePlugin ,
' enmod ' : self . enablePlugin ,
' disable-plugin ' : self . disablePlugin ,
' displugin ' : self . disablePlugin ,
' disableplugin ' : self . disablePlugin ,
' dismod ' : self . disablePlugin ,
' reload-plugin ' : self . reloadPlugin ,
' reloadplugin ' : self . reloadPlugin ,
' reload-plugins ' : self . reloadPlugin ,
' reloadplugins ' : self . reloadPlugin ,
2018-04-21 03:10:50 +00:00
' create-plugin ' : self . createPlugin ,
' createplugin ' : self . createPlugin ,
' plugin-create ' : self . createPlugin ,
2018-03-03 04:19:01 +00:00
2018-04-18 03:43:33 +00:00
' listkeys ' : self . listKeys ,
' list-keys ' : self . listKeys ,
2018-03-03 04:19:01 +00:00
2018-02-04 01:11:35 +00:00
' addpeer ' : self . addPeer ,
2018-02-27 21:23:49 +00:00
' add-peer ' : self . addPeer ,
' add-address ' : self . addAddress ,
2018-04-19 01:47:35 +00:00
' add-addr ' : self . addAddress ,
' addaddr ' : self . addAddress ,
2018-03-03 04:19:01 +00:00
' addaddress ' : self . addAddress ,
2018-07-01 21:01:19 +00:00
' list-peers ' : self . listPeers ,
2018-06-05 02:26:04 +00:00
2018-08-10 22:13:58 +00:00
' blacklist-block ' : self . banBlock ,
2018-06-01 04:25:28 +00:00
' add-file ' : self . addFile ,
2018-04-23 01:43:17 +00:00
' addfile ' : self . addFile ,
2018-09-04 18:56:05 +00:00
' get-file ' : self . getFile ,
' getfile ' : self . getFile ,
2018-06-13 22:22:48 +00:00
' listconn ' : self . listConn ,
2018-03-03 04:19:01 +00:00
2018-06-01 04:25:28 +00:00
' import-blocks ' : self . onionrUtils . importNewBlocks ,
2018-05-10 07:42:24 +00:00
' importblocks ' : self . onionrUtils . importNewBlocks ,
2018-04-21 03:10:50 +00:00
' introduce ' : self . onionrCore . introduceNode ,
2018-06-07 08:15:01 +00:00
' connect ' : self . addAddress ,
2018-07-03 23:44:12 +00:00
' kex ' : self . doKEX ,
2018-08-02 20:18:01 +00:00
' pex ' : self . doPEX ,
2018-06-07 08:15:01 +00:00
2018-08-04 02:52:45 +00:00
' ui ' : self . openUI ,
' gui ' : self . openUI ,
2018-08-05 04:09:33 +00:00
' getpassword ' : self . printWebPassword ,
' get-password ' : self . printWebPassword ,
' getpwd ' : self . printWebPassword ,
' get-pwd ' : self . printWebPassword ,
' getpass ' : self . printWebPassword ,
' get-pass ' : self . printWebPassword ,
' getpasswd ' : self . printWebPassword ,
2018-08-27 03:44:32 +00:00
' get-passwd ' : self . printWebPassword ,
2018-09-20 17:07:50 +00:00
' chat ' : self . startChat ,
2018-09-20 05:13:26 +00:00
2018-08-27 03:44:32 +00:00
' friend ' : self . friendCmd
2018-02-04 01:11:35 +00:00
}
2018-03-03 07:00:43 +00:00
self . cmdhelp = {
2018-02-22 07:24:25 +00:00
' help ' : ' Displays this Onionr help menu ' ,
' version ' : ' Displays the Onionr version ' ,
2018-02-23 02:25:05 +00:00
' config ' : ' Configures something and adds it to the file ' ,
2018-02-22 07:24:25 +00:00
' start ' : ' Starts the Onionr daemon ' ,
' stop ' : ' Stops the Onionr daemon ' ,
' stats ' : ' Displays node statistics ' ,
2018-08-05 04:09:33 +00:00
' get-password ' : ' Displays the web password ' ,
2018-03-03 04:19:01 +00:00
' enable-plugin ' : ' Enables and starts a plugin ' ,
' disable-plugin ' : ' Disables and stops a plugin ' ,
' reload-plugin ' : ' Reloads a plugin ' ,
2018-04-21 03:10:50 +00:00
' create-plugin ' : ' Creates directory structure for a plugin ' ,
2018-05-02 06:25:44 +00:00
' add-peer ' : ' Adds a peer to database ' ,
2018-04-19 01:47:35 +00:00
' list-peers ' : ' Displays a list of peers ' ,
2018-06-01 04:25:28 +00:00
' add-file ' : ' Create an Onionr block from a file ' ,
2018-09-04 18:56:05 +00:00
' get-file ' : ' Get a file from Onionr blocks ' ,
2018-06-01 04:25:28 +00:00
' import-blocks ' : ' import blocks from the disk (Onionr is transport-agnostic!) ' ,
2018-06-13 22:22:48 +00:00
' listconn ' : ' list connected peers ' ,
2018-07-03 23:44:12 +00:00
' kex ' : ' exchange keys with peers (done automatically) ' ,
2018-08-02 20:18:01 +00:00
' pex ' : ' exchange addresses with peers (done automatically) ' ,
2018-08-11 05:23:59 +00:00
' blacklist-block ' : ' deletes a block by hash and permanently removes it from your node ' ,
2018-05-02 06:25:44 +00:00
' introduce ' : ' Introduce your node to the public Onionr network ' ,
2018-08-27 03:44:32 +00:00
' friend ' : ' [add|remove] [public key/id] '
2018-02-22 07:24:25 +00:00
}
2018-04-21 03:10:50 +00:00
# initialize plugins
2018-05-06 23:00:28 +00:00
events . event ( ' init ' , onionr = self , threaded = False )
2018-04-21 03:10:50 +00:00
2018-03-03 07:00:43 +00:00
command = ' '
try :
command = sys . argv [ 1 ] . lower ( )
except IndexError :
command = ' '
finally :
self . execute ( command )
if not self . _developmentMode :
encryptionPassword = self . onionrUtils . getPassword ( ' Enter password to encrypt directory: ' )
self . onionrCore . dataDirEncrypt ( encryptionPassword )
shutil . rmtree ( ' data/ ' )
return
'''
THIS SECTION HANDLES THE COMMANDS
'''
2018-09-20 17:04:58 +00:00
def startChat ( self ) :
2018-09-23 04:53:09 +00:00
try :
data = json . dumps ( { ' peer ' : sys . argv [ 2 ] , ' reason ' : ' chat ' } )
except IndexError :
logger . error ( ' Must specify peer to chat with. ' )
else :
self . onionrCore . daemonQueueAdd ( ' startSocket ' , data )
2018-09-20 17:04:58 +00:00
2018-03-03 07:00:43 +00:00
def getCommands ( self ) :
return self . cmds
2018-08-27 03:44:32 +00:00
def friendCmd ( self ) :
2018-08-29 01:09:27 +00:00
''' List, add, or remove friend(s)
Changes their peer DB entry .
'''
2018-08-27 03:44:32 +00:00
friend = ' '
try :
2018-08-29 01:09:27 +00:00
# Get the friend command
2018-08-27 03:44:32 +00:00
action = sys . argv [ 2 ]
except IndexError :
logger . info ( ' Syntax: friend add/remove/list [address] ' )
else :
action = action . lower ( )
if action == ' list ' :
2018-08-29 01:09:27 +00:00
# List out peers marked as our friend
2018-08-27 03:44:32 +00:00
for friend in self . onionrCore . listPeers ( randomOrder = False , trust = 1 ) :
2018-08-29 01:09:27 +00:00
if friend == self . onionrCore . _crypto . pubKey : # do not list our key
2018-08-28 04:45:31 +00:00
continue
friendProfile = onionrusers . OnionrUser ( self . onionrCore , friend )
logger . info ( friend + ' - ' + friendProfile . getName ( ) )
2018-08-27 03:44:32 +00:00
elif action in ( ' add ' , ' remove ' ) :
try :
friend = sys . argv [ 3 ]
if not self . onionrUtils . validatePubKey ( friend ) :
raise onionrexceptions . InvalidPubkey ( ' Public key is invalid ' )
if friend not in self . onionrCore . listPeers ( ) :
raise onionrexceptions . KeyNotKnown
friend = onionrusers . OnionrUser ( self . onionrCore , friend )
except IndexError :
logger . error ( ' Friend ID is required. ' )
except onionrexceptions . KeyNotKnown :
logger . error ( ' That peer is not in our database ' )
else :
if action == ' add ' :
friend . setTrust ( 1 )
logger . info ( ' Added %s as friend. ' % ( friend . publicKey , ) )
else :
friend . setTrust ( 0 )
logger . info ( ' Removed %s as friend. ' % ( friend . publicKey , ) )
else :
logger . info ( ' Syntax: friend add/remove/list [address] ' )
2018-06-13 06:09:55 +00:00
2018-08-10 22:13:58 +00:00
def banBlock ( self ) :
try :
ban = sys . argv [ 2 ]
except IndexError :
2018-08-11 05:23:59 +00:00
ban = logger . readline ( ' Enter a block hash: ' )
if self . onionrUtils . validateHash ( ban ) :
if not self . onionrCore . _blacklist . inBlacklist ( ban ) :
try :
self . onionrCore . _blacklist . addToDB ( ban )
self . onionrCore . removeBlock ( ban )
except Exception as error :
logger . error ( ' Could not blacklist block ' , error = error )
else :
logger . info ( ' Block blacklisted ' )
else :
logger . warn ( ' That block is already blacklisted ' )
else :
logger . error ( ' Invalid block hash ' )
2018-08-10 22:13:58 +00:00
return
2018-06-13 22:22:48 +00:00
def listConn ( self ) :
self . onionrCore . daemonQueueAdd ( ' connectedPeers ' )
2018-07-06 04:27:12 +00:00
2018-07-01 21:01:19 +00:00
def listPeers ( self ) :
logger . info ( ' Peer transport address list: ' )
for i in self . onionrCore . listAdders ( ) :
logger . info ( i )
2018-06-13 22:22:48 +00:00
2018-06-07 08:15:01 +00:00
def getWebPassword ( self ) :
2018-06-14 04:17:58 +00:00
return config . get ( ' client.hmac ' )
2018-07-30 00:37:12 +00:00
2018-07-23 07:43:10 +00:00
def printWebPassword ( self ) :
print ( self . getWebPassword ( ) )
2018-03-03 07:00:43 +00:00
def getHelp ( self ) :
return self . cmdhelp
def addCommand ( self , command , function ) :
2018-04-21 01:20:26 +00:00
self . cmds [ str ( command ) . lower ( ) ] = function
2018-03-03 07:00:43 +00:00
def addHelp ( self , command , description ) :
2018-04-21 01:20:26 +00:00
self . cmdhelp [ str ( command ) . lower ( ) ] = str ( description )
2018-04-21 01:26:46 +00:00
2018-04-21 01:20:26 +00:00
def delCommand ( self , command ) :
return self . cmds . pop ( str ( command ) . lower ( ) , None )
2018-04-21 01:26:46 +00:00
2018-04-21 01:20:26 +00:00
def delHelp ( self , command ) :
return self . cmdhelp . pop ( str ( command ) . lower ( ) , None )
2018-03-03 07:00:43 +00:00
2018-02-23 02:25:05 +00:00
def configure ( self ) :
'''
Displays something from the configuration file , or sets it
'''
if len ( sys . argv ) > = 4 :
config . reload ( )
config . set ( sys . argv [ 2 ] , sys . argv [ 3 ] , True )
logger . debug ( ' Configuration file updated. ' )
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. ' ) ) )
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> ' )
2018-02-04 01:11:35 +00:00
def execute ( self , argument ) :
2018-02-22 07:24:25 +00:00
'''
Executes a command
'''
2018-04-21 03:10:50 +00:00
2018-02-04 01:11:35 +00:00
argument = argument [ argument . startswith ( ' -- ' ) and len ( ' -- ' ) : ] # remove -- if it starts with it
# define commands
commands = self . getCommands ( )
command = commands . get ( argument , self . notFound )
command ( )
2018-04-21 01:26:46 +00:00
2018-04-21 01:20:26 +00:00
return
2018-02-04 01:11:35 +00:00
'''
THIS SECTION DEFINES THE COMMANDS
'''
2018-02-22 07:24:25 +00:00
def version ( self , verbosity = 5 ) :
'''
Displays the Onionr version
'''
2018-04-21 03:10:50 +00:00
2018-05-02 06:01:20 +00:00
logger . info ( ' Onionr %s ( %s ) - API v %s ' % ( ONIONR_VERSION , platform . machine ( ) , API_VERSION ) )
2018-02-22 07:24:25 +00:00
if verbosity > = 1 :
2018-03-03 07:10:27 +00:00
logger . info ( ONIONR_TAGLINE )
2018-02-22 07:24:25 +00:00
if verbosity > = 2 :
2018-05-02 06:01:20 +00:00
logger . info ( ' Running on %s %s ' % ( platform . platform ( ) , platform . release ( ) ) )
2018-04-21 01:26:46 +00:00
2018-04-21 01:20:26 +00:00
return
2018-07-06 04:27:12 +00:00
2018-07-03 23:44:12 +00:00
def doKEX ( self ) :
''' make communicator do kex '''
logger . info ( ' Sending kex to command queue... ' )
self . onionrCore . daemonQueueAdd ( ' kex ' )
2018-02-04 01:11:35 +00:00
2018-08-02 20:18:01 +00:00
def doPEX ( self ) :
''' make communicator do pex '''
logger . info ( ' Sending pex to command queue... ' )
self . onionrCore . daemonQueueAdd ( ' pex ' )
2018-04-18 03:43:33 +00:00
def listKeys ( self ) :
2018-02-22 07:24:25 +00:00
'''
2018-04-18 03:43:33 +00:00
Displays a list of keys ( used to be called peers ) ( ? )
2018-02-22 07:24:25 +00:00
'''
2018-04-18 03:43:33 +00:00
logger . info ( ' Public keys in database: \n ' )
2018-02-04 01:11:35 +00:00
for i in self . onionrCore . listPeers ( ) :
logger . info ( i )
def addPeer ( self ) :
2018-02-22 07:24:25 +00:00
'''
Adds a peer ( ? )
'''
2018-02-04 01:11:35 +00:00
try :
newPeer = sys . argv [ 2 ]
except :
pass
else :
2018-06-09 06:21:14 +00:00
if self . onionrUtils . hasKey ( newPeer ) :
logger . info ( ' We already have that key ' )
return
if not ' - ' in newPeer :
logger . info ( ' Since no POW token was supplied for that key, one is being generated ' )
2018-07-13 06:14:09 +00:00
proof = onionrproofs . DataPOW ( newPeer )
2018-06-09 06:21:14 +00:00
while True :
result = proof . getResult ( )
if result == False :
time . sleep ( 0.5 )
else :
break
newPeer + = ' - ' + base64 . b64encode ( result [ 1 ] ) . decode ( )
logger . info ( newPeer )
2018-02-04 01:11:35 +00:00
logger . info ( " Adding peer: " + logger . colors . underline + newPeer )
2018-06-09 06:21:14 +00:00
if self . onionrUtils . mergeKeys ( newPeer ) :
logger . info ( ' Successfully added key ' )
else :
logger . error ( ' Failed to add key ' )
2018-03-03 04:19:01 +00:00
return
2018-02-27 21:23:49 +00:00
def addAddress ( self ) :
2018-04-19 01:47:35 +00:00
'''
Adds a Onionr node address
'''
2018-05-02 06:01:20 +00:00
2018-02-27 21:23:49 +00:00
try :
newAddress = sys . argv [ 2 ]
except :
pass
else :
logger . info ( " Adding address: " + logger . colors . underline + newAddress )
if self . onionrCore . addAddress ( newAddress ) :
2018-04-19 01:47:35 +00:00
logger . info ( " Successfully added address. " )
2018-02-27 21:23:49 +00:00
else :
2018-04-19 01:47:35 +00:00
logger . warn ( " Unable to add address. " )
2018-02-04 01:11:35 +00:00
2018-03-03 04:19:01 +00:00
return
2018-04-16 02:22:19 +00:00
def addMessage ( self , header = " txt " ) :
2018-02-22 07:24:25 +00:00
'''
Broadcasts a message to the Onionr network
'''
2018-02-04 01:11:35 +00:00
while True :
2018-05-02 06:01:20 +00:00
try :
messageToAdd = logger . readline ( ' Broadcast message to network: ' )
if len ( messageToAdd ) > = 1 :
break
except KeyboardInterrupt :
return
2018-02-22 07:24:25 +00:00
2018-06-21 07:29:51 +00:00
#addedHash = Block(type = 'txt', content = messageToAdd).save()
addedHash = self . onionrCore . insertBlock ( messageToAdd )
2018-07-10 03:09:45 +00:00
if addedHash != None and addedHash != False and addedHash != " " :
2018-05-07 06:55:03 +00:00
logger . info ( " Message inserted as as block %s " % addedHash )
2018-06-01 04:25:28 +00:00
else :
logger . error ( ' Failed to insert block. ' , timestamp = False )
2018-03-03 04:19:01 +00:00
return
2018-04-19 01:47:35 +00:00
2018-03-03 04:19:01 +00:00
def enablePlugin ( self ) :
'''
Enables and starts the given plugin
'''
if len ( sys . argv ) > = 3 :
plugin_name = sys . argv [ 2 ]
2018-05-02 06:01:20 +00:00
logger . info ( ' Enabling plugin " %s " ... ' % plugin_name )
2018-04-21 01:20:26 +00:00
plugins . enable ( plugin_name , self )
2018-03-03 04:19:01 +00:00
else :
2018-05-02 06:01:20 +00:00
logger . info ( ' %s %s <plugin> ' % ( sys . argv [ 0 ] , sys . argv [ 1 ] ) )
2018-03-03 04:19:01 +00:00
return
def disablePlugin ( self ) :
'''
Disables and stops the given plugin
'''
if len ( sys . argv ) > = 3 :
plugin_name = sys . argv [ 2 ]
2018-05-02 06:01:20 +00:00
logger . info ( ' Disabling plugin " %s " ... ' % plugin_name )
2018-04-21 01:20:26 +00:00
plugins . disable ( plugin_name , self )
2018-03-03 04:19:01 +00:00
else :
2018-05-02 06:01:20 +00:00
logger . info ( ' %s %s <plugin> ' % ( sys . argv [ 0 ] , sys . argv [ 1 ] ) )
2018-03-03 04:19:01 +00:00
return
def reloadPlugin ( self ) :
'''
Reloads ( stops and starts ) all plugins , or the given plugin
'''
if len ( sys . argv ) > = 3 :
plugin_name = sys . argv [ 2 ]
2018-05-02 06:01:20 +00:00
logger . info ( ' Reloading plugin " %s " ... ' % plugin_name )
2018-04-21 01:20:26 +00:00
plugins . stop ( plugin_name , self )
plugins . start ( plugin_name , self )
2018-03-03 04:19:01 +00:00
else :
logger . info ( ' Reloading all plugins... ' )
2018-04-21 01:20:26 +00:00
plugins . reload ( self )
2018-03-03 04:19:01 +00:00
return
2018-04-21 03:10:50 +00:00
def createPlugin ( self ) :
'''
Creates the directory structure for a plugin name
'''
if len ( sys . argv ) > = 3 :
try :
plugin_name = re . sub ( ' [^0-9a-zA-Z]+ ' , ' ' , str ( sys . argv [ 2 ] ) . lower ( ) )
if not plugins . exists ( plugin_name ) :
2018-05-02 06:01:20 +00:00
logger . info ( ' Creating plugin " %s " ... ' % plugin_name )
2018-04-21 03:10:50 +00:00
os . makedirs ( plugins . get_plugins_folder ( plugin_name ) )
with open ( plugins . get_plugins_folder ( plugin_name ) + ' /main.py ' , ' a ' ) as main :
2018-06-05 02:26:04 +00:00
contents = ' '
with open ( ' static-data/default_plugin.py ' , ' rb ' ) as file :
2018-06-07 01:54:35 +00:00
contents = file . read ( ) . decode ( )
2018-06-05 02:26:04 +00:00
# TODO: Fix $user. os.getlogin() is B U G G Y
main . write ( contents . replace ( ' $user ' , ' some random developer ' ) . replace ( ' $date ' , datetime . datetime . now ( ) . strftime ( ' % Y- % m- %d ' ) ) . replace ( ' $name ' , plugin_name ) )
2018-05-14 04:11:31 +00:00
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 ' } ) )
2018-04-21 03:10:50 +00:00
2018-05-02 06:01:20 +00:00
logger . info ( ' Enabling plugin " %s " ... ' % plugin_name )
2018-04-21 03:10:50 +00:00
plugins . enable ( plugin_name , self )
else :
2018-05-02 06:01:20 +00:00
logger . warn ( ' Cannot create plugin directory structure; plugin " %s " exists. ' % plugin_name )
2018-04-21 03:10:50 +00:00
except Exception as e :
logger . error ( ' Failed to create plugin directory structure. ' , e )
else :
2018-05-02 06:01:20 +00:00
logger . info ( ' %s %s <plugin> ' % ( sys . argv [ 0 ] , sys . argv [ 1 ] ) )
2018-04-21 03:10:50 +00:00
return
2018-02-04 01:11:35 +00:00
def notFound ( self ) :
2018-02-22 07:24:25 +00:00
'''
Displays a " command not found " message
'''
2018-04-19 02:25:16 +00:00
logger . error ( ' Command not found. ' , timestamp = False )
2018-02-04 01:11:35 +00:00
def showHelpSuggestion ( self ) :
2018-02-22 07:24:25 +00:00
'''
Displays a message suggesting help
'''
2018-02-04 01:11:35 +00:00
logger . info ( ' Do ' + logger . colors . bold + sys . argv [ 0 ] + ' --help ' + logger . colors . reset + logger . colors . fg . green + ' for Onionr help. ' )
2018-06-14 04:17:58 +00:00
def start ( self , input = False , override = False ) :
2018-02-22 07:24:25 +00:00
'''
Starts the Onionr daemon
'''
2018-06-14 04:17:58 +00:00
if os . path . exists ( ' .onionr-lock ' ) and not override :
2018-02-04 01:11:35 +00:00
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). ' )
else :
if not self . debug and not self . _developmentMode :
lockFile = open ( ' .onionr-lock ' , ' w ' )
lockFile . write ( ' ' )
lockFile . close ( )
2018-04-22 00:09:48 +00:00
self . running = True
2018-02-04 01:11:35 +00:00
self . daemon ( )
2018-04-22 00:09:48 +00:00
self . running = False
2018-02-04 01:11:35 +00:00
if not self . debug and not self . _developmentMode :
os . remove ( ' .onionr-lock ' )
2017-12-26 07:25:29 +00:00
def daemon ( self ) :
2018-02-22 07:24:25 +00:00
'''
Starts the Onionr communication daemon
'''
2018-07-13 04:57:03 +00:00
communicatorDaemon = ' ./communicator2.py '
2018-02-04 01:11:35 +00:00
2018-08-06 07:50:08 +00:00
apiThread = Thread ( target = api . API , args = ( self . debug , ) )
apiThread . start ( )
try :
time . sleep ( 3 )
except KeyboardInterrupt :
logger . info ( ' Got keyboard interrupt ' )
time . sleep ( 1 )
self . onionrUtils . localCommand ( ' shutdown ' )
else :
if apiThread . isAlive ( ) :
if self . _developmentMode :
logger . warn ( ' DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!) ' , timestamp = False )
net = NetController ( config . get ( ' client.port ' , 59496 ) )
logger . info ( ' Tor is starting... ' )
if not net . startTor ( ) :
sys . exit ( 1 )
logger . info ( ' Started .onion service: ' + logger . colors . underline + net . myID )
logger . info ( ' Our Public key: ' + self . onionrCore . _crypto . pubKey )
time . sleep ( 1 )
#TODO make runable on windows
subprocess . Popen ( [ communicatorDaemon , " run " , str ( net . socksPort ) ] )
2018-08-23 17:48:49 +00:00
# Print nice header thing :)
if config . get ( ' general.display_header ' , True ) :
self . header ( )
2018-08-06 07:50:08 +00:00
logger . debug ( ' Started communicator ' )
events . event ( ' daemon_start ' , onionr = self )
try :
while True :
time . sleep ( 5 )
except KeyboardInterrupt :
self . onionrCore . daemonQueueAdd ( ' shutdown ' )
self . onionrUtils . localCommand ( ' shutdown ' )
2017-12-26 07:25:29 +00:00
return
2018-02-04 01:11:35 +00:00
2017-12-26 07:25:29 +00:00
def killDaemon ( self ) :
2018-02-22 07:24:25 +00:00
'''
Shutdown the Onionr daemon
'''
2018-02-04 01:11:35 +00:00
2018-05-02 06:35:08 +00:00
logger . warn ( ' Killing the running daemon... ' , timestamp = False )
2018-01-14 08:48:23 +00:00
try :
2018-05-02 06:35:08 +00:00
events . event ( ' daemon_stop ' , onionr = self )
2018-06-14 04:17:58 +00:00
net = NetController ( config . get ( ' client.port ' , 59496 ) )
2018-07-09 07:02:33 +00:00
try :
self . onionrCore . daemonQueueAdd ( ' shutdown ' )
except sqlite3 . OperationalError :
pass
2018-05-02 06:35:08 +00:00
net . killTor ( )
except Exception as e :
logger . error ( ' Failed to shutdown daemon. ' , error = e , timestamp = False )
2018-02-04 01:11:35 +00:00
2017-12-26 07:25:29 +00:00
return
2018-02-04 01:11:35 +00:00
2017-12-26 07:25:29 +00:00
def showStats ( self ) :
2018-02-22 07:24:25 +00:00
'''
Displays statistics and exits
'''
2018-05-04 07:14:27 +00:00
2018-05-11 02:05:56 +00:00
try :
# define stats messages here
2018-06-01 04:25:28 +00:00
totalBlocks = len ( Block . getBlocks ( ) )
signedBlocks = len ( Block . getBlocks ( signed = True ) )
2018-06-09 06:21:14 +00:00
powToken = self . onionrCore . _crypto . pubKeyPowToken
2018-05-11 02:05:56 +00:00
messages = {
# info about local client
2018-05-11 03:19:48 +00:00
' Onionr Daemon Status ' : ( ( logger . colors . fg . green + ' Online ' ) if self . onionrUtils . isCommunicatorRunning ( timeout = 2 ) else logger . colors . fg . red + ' Offline ' ) ,
2018-05-11 02:05:56 +00:00
' Public Key ' : self . onionrCore . _crypto . pubKey ,
2018-06-09 06:21:14 +00:00
' POW Token ' : powToken ,
' Combined ' : self . onionrCore . _crypto . pubKey + ' - ' + powToken ,
2018-07-03 19:26:53 +00:00
' Human readable public key ' : self . onionrCore . _utils . getHumanReadableID ( ) ,
2018-06-09 06:21:14 +00:00
' Node Address ' : self . get_hostname ( ) ,
2018-05-11 02:05:56 +00:00
# file and folder size stats
' div1 ' : True , # this creates a solid line across the screen, a div
' Total Block Size ' : onionrutils . humanSize ( onionrutils . size ( ' data/blocks/ ' ) ) ,
' Total Plugin Size ' : onionrutils . humanSize ( onionrutils . size ( ' data/plugins/ ' ) ) ,
' Log File Size ' : onionrutils . humanSize ( onionrutils . size ( ' data/output.log ' ) ) ,
# count stats
' div2 ' : True ,
2018-05-19 21:32:21 +00:00
' Known Peers Count ' : str ( len ( self . onionrCore . listPeers ( ) ) - 1 ) ,
2018-06-14 04:17:58 +00:00
' Enabled Plugins Count ' : str ( len ( config . get ( ' plugins.enabled ' , list ( ) ) ) ) + ' / ' + str ( len ( os . listdir ( ' data/plugins/ ' ) ) ) ,
2018-06-01 04:25:28 +00:00
' Known Blocks Count ' : str ( totalBlocks ) ,
2018-06-13 05:50:55 +00:00
' Percent Blocks Signed ' : str ( round ( 100 * signedBlocks / max ( totalBlocks , 1 ) , 2 ) ) + ' % '
2018-05-11 02:05:56 +00:00
}
2018-05-11 02:20:14 +00:00
# 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
}
2018-05-11 02:05:56 +00:00
# pre-processing
maxlength = 0
2018-06-13 06:09:55 +00:00
width = self . getConsoleWidth ( )
2018-05-11 02:05:56 +00:00
for key , val in messages . items ( ) :
if not ( type ( val ) is bool and val is True ) :
maxlength = max ( len ( key ) , maxlength )
2018-06-13 06:09:55 +00:00
prewidth = maxlength + len ( ' | ' )
groupsize = width - prewidth - len ( ' [+] ' )
2018-05-11 02:05:56 +00:00
# generate stats table
2018-05-11 02:20:14 +00:00
logger . info ( colors [ ' title ' ] + ' Onionr v %s Statistics ' % ONIONR_VERSION + colors [ ' reset ' ] )
2018-06-05 05:26:11 +00:00
logger . info ( colors [ ' border ' ] + ' - ' * ( maxlength + 1 ) + ' + ' + colors [ ' reset ' ] )
2018-05-11 02:05:56 +00:00
for key , val in messages . items ( ) :
if not ( type ( val ) is bool and val is True ) :
2018-06-13 06:09:55 +00:00
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 ' ] )
2018-05-11 02:05:56 +00:00
else :
2018-06-05 05:26:11 +00:00
logger . info ( colors [ ' border ' ] + ' - ' * ( maxlength + 1 ) + ' + ' + colors [ ' reset ' ] )
logger . info ( colors [ ' border ' ] + ' - ' * ( maxlength + 1 ) + ' + ' + colors [ ' reset ' ] )
2018-05-11 02:05:56 +00:00
except Exception as e :
logger . error ( ' Failed to generate statistics table. ' , error = e , timestamp = False )
2017-12-26 07:25:29 +00:00
return
2018-02-04 01:11:35 +00:00
2018-02-22 07:24:25 +00:00
def showHelp ( self , command = None ) :
'''
Show help for Onionr
'''
helpmenu = self . getHelp ( )
2018-02-04 01:11:35 +00:00
2018-02-22 07:24:25 +00:00
if command is None and len ( sys . argv ) > = 3 :
for cmd in sys . argv [ 2 : ] :
self . showHelp ( cmd )
elif not command is None :
if command . lower ( ) in helpmenu :
2018-04-22 00:09:48 +00:00
logger . info ( logger . colors . bold + command + logger . colors . reset + logger . colors . fg . blue + ' : ' + logger . colors . reset + helpmenu [ command . lower ( ) ] , timestamp = False )
2018-02-22 07:24:25 +00:00
else :
2018-04-22 00:09:48 +00:00
logger . warn ( logger . colors . bold + command + logger . colors . reset + logger . colors . fg . blue + ' : ' + logger . colors . reset + ' No help menu entry was found ' , timestamp = False )
2018-02-22 07:24:25 +00:00
else :
self . version ( 0 )
for command , helpmessage in helpmenu . items ( ) :
self . showHelp ( command )
2017-12-26 07:25:29 +00:00
return
2018-02-04 01:11:35 +00:00
2018-04-22 00:09:48 +00:00
def get_hostname ( self ) :
2018-04-22 01:53:12 +00:00
try :
with open ( ' ./data/hs/hostname ' , ' r ' ) as hostname :
2018-04-23 03:49:53 +00:00
return hostname . read ( ) . strip ( )
except Exception :
return None
2018-04-23 03:45:25 +00:00
2018-06-13 06:09:55 +00:00
def getConsoleWidth ( self ) :
'''
Returns an integer , the width of the terminal / cmd window
'''
columns = 80
try :
columns = int ( os . popen ( ' stty size ' , ' r ' ) . read ( ) . split ( ) [ 1 ] )
except :
# if it errors, it's probably windows, so default to 80.
pass
return columns
2018-09-04 18:56:05 +00:00
def getFile ( self ) :
'''
Get a file from onionr blocks
'''
if len ( sys . argv ) > = 3 :
fileName = sys . argv [ 2 ]
print ( fileName )
contents = None
bHash = sys . argv [ 3 ]
if os . path . exists ( fileName ) :
logger . error ( " File already exists " )
return
if not self . onionrUtils . validateHash ( bHash ) :
logger . error ( ' Block hash is invalid ' )
return
Block . mergeChain ( bHash , fileName )
return
2018-04-23 01:43:17 +00:00
def addFile ( self ) :
2018-06-01 04:25:28 +00:00
'''
Adds a file to the onionr network
'''
if len ( sys . argv ) > = 3 :
filename = sys . argv [ 2 ]
contents = None
2018-06-05 02:26:04 +00:00
2018-06-01 04:25:28 +00:00
if not os . path . exists ( filename ) :
2018-09-04 18:56:05 +00:00
logger . error ( ' That file does not exist. Improper path (specify full path)? ' )
return
logger . info ( ' Adding file... this might take a long time. ' )
2018-06-01 04:25:28 +00:00
try :
2018-06-05 05:26:11 +00:00
blockhash = Block . createChain ( file = filename )
2018-06-01 04:25:28 +00:00
logger . info ( ' File %s saved in block %s . ' % ( filename , blockhash ) )
2018-06-05 05:26:11 +00:00
except :
2018-06-01 04:25:28 +00:00
logger . error ( ' Failed to save file in block. ' , timestamp = False )
else :
logger . error ( ' %s add-file <filename> ' % sys . argv [ 0 ] , timestamp = False )
2018-04-23 03:45:25 +00:00
2018-08-04 02:52:45 +00:00
def openUI ( self ) :
url = ' http://127.0.0.1: %s /ui/index.html?timingToken= %s ' % ( config . get ( ' client.port ' , 59496 ) , self . onionrUtils . getTimeBypassToken ( ) )
print ( ' Opening %s ... ' % url )
webbrowser . open ( url , new = 1 , autoraise = True )
2018-08-23 17:48:49 +00:00
def header ( self , message = logger . colors . fg . pink + logger . colors . bold + ' Onionr ' + logger . colors . reset + logger . colors . fg . pink + ' has started. ' ) :
if os . path . exists ( ' static-data/header.txt ' ) :
with open ( ' static-data/header.txt ' , ' rb ' ) as file :
# only to stdout, not file or log or anything
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 ( ' V ' , ONIONR_VERSION ) )
logger . info ( logger . colors . fg . lightgreen + ' -> ' + str ( message ) + logger . colors . reset + logger . colors . fg . lightgreen + ' <- \n ' )
2018-07-06 04:27:12 +00:00
if __name__ == " __main__ " :
Onionr ( )