2017-12-26 07:25:29 +00:00
#!/usr/bin/env python3
'''
2019-06-01 16:54:36 +00:00
Onionr - Private P2P Communication
2018-01-14 00:07:13 +00:00
2019-06-01 16:54:36 +00:00
This file initializes Onionr when ran to be a daemon or with commands
2018-01-14 00:07:13 +00:00
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-05-01 07:25:31 +00:00
import sys
2019-06-25 08:21:36 +00:00
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.
2019-01-16 05:57:47 +00:00
MIN_PY_VERSION = 6
if sys . version_info [ 0 ] == 2 or sys . version_info [ 1 ] < MIN_PY_VERSION :
2019-06-27 06:36:52 +00:00
sys . stderr . write ( ' Error, Onionr requires Python 3. %s + \n ' % ( MIN_PY_VERSION , ) )
2018-05-01 07:25:31 +00:00
sys . exit ( 1 )
2019-06-27 06:36:52 +00:00
from utils import detectoptimization
if detectoptimization . detect_optimization ( ) :
sys . stderr . write ( ' Error, Onionr cannot be run in optimized mode \n ' )
sys . exit ( 1 )
2019-07-20 15:52:03 +00:00
from utils import createdirs
createdirs . create_dirs ( )
2019-06-11 06:08:44 +00:00
import os , base64 , random , shutil , time , platform , signal
2018-04-22 00:09:48 +00:00
from threading import Thread
2019-07-19 19:49:56 +00:00
import config , logger , onionrplugins as plugins , onionrevents as events
2019-06-01 16:54:36 +00:00
import netcontroller
2018-01-19 09:16:38 +00:00
from netcontroller import NetController
2018-06-01 04:25:28 +00:00
from onionrblockapi import Block
2019-03-09 01:57:44 +00:00
import onionrproofs , onionrexceptions , communicator , setupconfig
2019-03-08 01:08:06 +00:00
import onionrcommands as commands # Many command definitions are here
2019-07-20 15:52:03 +00:00
from utils import identifyhome
2019-07-19 19:49:56 +00:00
from coredb import keydb
import filepaths
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 :
2019-07-15 03:01:56 +00:00
raise ImportError ( " You need the PySocks module (for use with socks5 proxy to use Tor) " )
2018-01-28 01:53:24 +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
'''
2019-06-25 23:07:35 +00:00
self . API_VERSION = API_VERSION
2018-09-04 18:56:05 +00:00
self . userRunDir = os . getcwd ( ) # Directory user runs the program from
2019-01-09 16:54:35 +00:00
self . killed = False
2019-07-12 07:07:30 +00:00
self . config = config
2019-02-19 22:14:06 +00:00
if sys . argv [ 0 ] == os . path . basename ( __file__ ) :
try :
os . chdir ( sys . path [ 0 ] )
except FileNotFoundError :
pass
2018-02-04 01:11:35 +00:00
2019-03-29 03:33:14 +00:00
# set data dir
2019-07-15 03:01:56 +00:00
self . dataDir = identifyhome . identify_home ( )
2019-03-29 03:33:14 +00:00
if not self . dataDir . endswith ( ' / ' ) :
self . dataDir + = ' / '
2018-02-23 01:58:36 +00:00
# Load global configuration data
2019-07-24 16:32:23 +00:00
data_exists = Onionr . setupConfig ( self )
2017-12-27 01:13:19 +00:00
2019-07-14 06:51:43 +00:00
if netcontroller . tor_binary ( ) is None :
2019-06-25 08:21:36 +00:00
logger . error ( ' Tor is not installed ' , terminal = True )
2019-01-18 05:34:13 +00:00
sys . exit ( 1 )
2019-05-12 14:41:36 +00:00
# If block data folder does not exist
if not os . path . exists ( self . dataDir + ' blocks/ ' ) :
os . mkdir ( self . dataDir + ' blocks/ ' )
2018-10-30 22:22:06 +00:00
# Copy default plugins into plugins folder
if not os . path . exists ( plugins . get_plugins_folder ( ) ) :
if os . path . exists ( ' static-data/default-plugins/ ' ) :
2019-03-07 04:58:21 +00:00
names = [ f for f in os . listdir ( " static-data/default-plugins/ " ) ]
2018-10-30 22:22:06 +00:00
shutil . copytree ( ' static-data/default-plugins/ ' , plugins.get_plugins_folder())
# Enable plugins
for name in names :
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 ) )
2019-07-15 03:01:56 +00:00
except Exception as e :
logger . warn ( ' Error enabling plugin: ' + str ( e ) )
2018-04-23 04:25:54 +00:00
plugins . disable ( name , onionr = self , stop_event = False )
2018-04-23 03:42:37 +00:00
2019-03-07 04:58:21 +00:00
self . communicatorInst = None
#self.deleteRunFiles()
self . clientAPIInst = ' ' # Client http api instance
self . publicAPIInst = ' ' # Public http api instance
signal . signal ( signal . SIGTERM , self . exitSigterm )
# Handle commands
self . debug = False # Whole application debugging
2017-12-27 05:00:02 +00:00
# Get configuration
2018-12-09 17:29:39 +00:00
if type ( config . get ( ' client.webpassword ' ) ) is type ( None ) :
config . set ( ' client.webpassword ' , base64 . b16encode ( os . urandom ( 32 ) ) . decode ( ' utf-8 ' ) , savefile = True )
2019-01-08 05:51:39 +00:00
if type ( config . get ( ' client.client.port ' ) ) is type ( None ) :
2019-07-14 07:05:25 +00:00
randomPort = netcontroller . get_open_port ( )
2019-01-08 05:51:39 +00:00
config . set ( ' client.client.port ' , randomPort , savefile = True )
if type ( config . get ( ' client.public.port ' ) ) is type ( None ) :
2019-07-14 07:05:25 +00:00
randomPort = netcontroller . get_open_port ( )
2019-01-08 05:51:39 +00:00
config . set ( ' client.public.port ' , randomPort , savefile = True )
2018-08-05 06:35:49 +00:00
if type ( config . get ( ' client.api_version ' ) ) is type ( None ) :
config . set ( ' client.api_version ' , API_VERSION , savefile = True )
2018-09-07 04:57:10 +00:00
2019-03-08 01:08:06 +00:00
self . cmds = commands . get_commands ( self )
self . cmdhelp = commands . cmd_help
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 )
2019-07-15 03:01:56 +00:00
os . chdir ( self . userRunDir )
2018-03-03 07:00:43 +00:00
return
2019-03-29 03:33:14 +00:00
2019-01-09 16:54:35 +00:00
def exitSigterm ( self , signum , frame ) :
self . killed = True
2018-03-03 07:00:43 +00:00
2019-07-24 16:32:23 +00:00
def setupConfig ( self ) :
return setupconfig . setup_config ( self )
2019-03-09 01:57:44 +00:00
2019-03-29 03:33:14 +00:00
def cmdHeader ( self ) :
if len ( sys . argv ) > = 3 :
self . header ( logger . colors . fg . pink + sys . argv [ 2 ] . replace ( ' Onionr ' , logger . colors . bold + ' Onionr ' + logger . colors . reset + logger . colors . fg . pink ) )
else :
self . header ( None )
2019-03-09 01:57:44 +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 ' ) and logger . get_level ( ) < = logger . LEVEL_INFO :
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 ( ' A ' , ' %s ' % API_VERSION ) . replace ( ' V ' , ONIONR_VERSION ) )
2019-03-29 03:33:14 +00:00
if not message is None :
2019-06-19 20:29:27 +00:00
logger . info ( logger . colors . fg . lightgreen + ' -> ' + str ( message ) + logger . colors . reset + logger . colors . fg . lightgreen + ' <- \n ' , terminal = True )
2018-03-03 07:00:43 +00:00
2019-03-09 01:57:44 +00:00
def deleteRunFiles ( self ) :
try :
2019-07-19 19:49:56 +00:00
os . remove ( filepaths . public_API_host_file )
2019-03-09 01:57:44 +00:00
except FileNotFoundError :
pass
try :
2019-07-19 19:49:56 +00:00
os . remove ( filepaths . private_API_host_file )
2019-03-09 01:57:44 +00:00
except FileNotFoundError :
pass
def get_hostname ( self ) :
try :
2019-07-15 03:01:56 +00:00
with open ( self . dataDir + ' hs/hostname ' , ' r ' ) as hostname :
2019-03-09 01:57:44 +00:00
return hostname . read ( ) . strip ( )
except FileNotFoundError :
return " Not Generated "
except Exception :
return None
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
'''
2019-06-01 16:54:36 +00:00
Handle command line commands
2019-03-09 01:57:44 +00:00
'''
2019-01-30 06:10:29 +00:00
def exportBlock ( self ) :
2019-06-20 00:59:05 +00:00
commands . exportblocks . export_block ( self )
2019-01-30 06:10:29 +00:00
2018-11-10 07:17:19 +00:00
def showDetails ( self ) :
2019-03-08 01:08:06 +00:00
commands . onionrstatistics . show_details ( self )
2019-03-29 03:33:14 +00:00
2019-01-13 22:20:10 +00:00
def openHome ( self ) :
2019-06-12 20:12:56 +00:00
commands . openwebinterface . open_home ( self )
2019-01-13 22:20:10 +00:00
2018-12-09 17:29:39 +00:00
def addID ( self ) :
2019-03-08 01:08:06 +00:00
commands . pubkeymanager . add_ID ( self )
2019-03-29 03:33:14 +00:00
2018-12-09 17:29:39 +00:00
def changeID ( self ) :
2019-03-08 01:08:06 +00:00
commands . pubkeymanager . change_ID ( self )
2018-12-09 17:29:39 +00:00
2018-03-03 07:00:43 +00:00
def getCommands ( self ) :
return self . cmds
2018-09-07 04:57:10 +00:00
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 .
'''
2019-03-08 01:08:06 +00:00
commands . pubkeymanager . friend_command ( self )
2018-11-11 02:10:58 +00:00
2018-08-10 22:13:58 +00:00
def banBlock ( self ) :
2019-06-01 16:54:36 +00:00
commands . banblocks . ban_block ( self )
2018-08-10 22:13:58 +00:00
2018-06-13 22:22:48 +00:00
def listConn ( self ) :
2019-03-08 06:30:14 +00:00
commands . onionrstatistics . show_peers ( self )
2018-07-06 04:27:12 +00:00
2018-07-01 21:01:19 +00:00
def listPeers ( self ) :
2019-07-21 00:29:08 +00:00
logger . info ( ' Peer transport address list: ' , terminal = True )
2019-07-19 19:49:56 +00:00
for i in keydb . listkeys . list_adders ( ) :
2019-06-27 18:45:28 +00:00
logger . info ( i , terminal = True )
2018-06-13 22:22:48 +00:00
2018-06-07 08:15:01 +00:00
def getWebPassword ( self ) :
2018-12-09 17:29:39 +00:00
return config . get ( ' client.webpassword ' )
2018-07-30 00:37:12 +00:00
2018-07-23 07:43:10 +00:00
def printWebPassword ( self ) :
2019-06-27 18:45:28 +00:00
logger . info ( self . getWebPassword ( ) , terminal = True )
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 )
2019-06-26 06:34:34 +00:00
logger . info ( ' Configuration file updated. ' , terminal = True )
2018-02-23 02:25:05 +00:00
elif len ( sys . argv ) > = 3 :
config . reload ( )
2019-06-26 06:34:34 +00:00
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 )
2018-02-23 02:25:05 +00:00
else :
2019-06-26 06:34:34 +00:00
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 )
2018-02-23 02:25:05 +00:00
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-11-11 03:25:40 +00:00
def version ( self , verbosity = 5 , function = logger . info ) :
2018-02-22 07:24:25 +00:00
'''
Displays the Onionr version
'''
2018-04-21 03:10:50 +00:00
2019-06-19 20:29:27 +00:00
function ( ' Onionr v %s ( %s ) (API v %s ) ' % ( ONIONR_VERSION , platform . machine ( ) , API_VERSION ) , terminal = True )
2018-02-22 07:24:25 +00:00
if verbosity > = 1 :
2019-06-19 20:29:27 +00:00
function ( ONIONR_TAGLINE , terminal = True )
2018-02-22 07:24:25 +00:00
if verbosity > = 2 :
2019-06-19 20:29:27 +00:00
function ( ' Running on %s %s ' % ( platform . platform ( ) , platform . release ( ) ) , terminal = True )
2018-04-21 01:26:46 +00:00
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
'''
2019-07-19 19:49:56 +00:00
logger . info ( ' %s Public keys in database: \n %s %s ' % ( logger . colors . fg . lightgreen , logger . colors . fg . green , ' \n ' . join ( keydb . listkeys . list_peers ( ) ( ) ) ) , terminal = True )
2018-02-04 01:11:35 +00:00
def addPeer ( self ) :
2018-02-22 07:24:25 +00:00
'''
Adds a peer ( ? )
'''
2019-03-09 01:57:44 +00:00
commands . keyadders . add_peer ( self )
2018-03-03 04:19:01 +00:00
2018-02-27 21:23:49 +00:00
def addAddress ( self ) :
2018-04-19 01:47:35 +00:00
'''
Adds a Onionr node address
'''
2019-03-09 01:57:44 +00:00
commands . keyadders . add_address ( self )
2018-03-03 04:19:01 +00:00
def enablePlugin ( self ) :
'''
Enables and starts the given plugin
'''
2019-03-09 01:57:44 +00:00
commands . plugincommands . enable_plugin ( self )
2018-03-03 04:19:01 +00:00
def disablePlugin ( self ) :
'''
Disables and stops the given plugin
'''
2019-03-09 01:57:44 +00:00
commands . plugincommands . disable_plugin ( self )
2018-03-03 04:19:01 +00:00
def reloadPlugin ( self ) :
'''
Reloads ( stops and starts ) all plugins , or the given plugin
'''
2019-03-09 01:57:44 +00:00
commands . plugincommands . reload_plugin ( self )
2018-03-03 04:19:01 +00:00
2018-04-21 03:10:50 +00:00
def createPlugin ( self ) :
'''
Creates the directory structure for a plugin name
'''
2019-03-09 01:57:44 +00:00
commands . plugincommands . create_plugin ( self )
2018-04-21 03:10:50 +00:00
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
'''
2019-06-20 00:59:05 +00:00
logger . error ( ' Command not found. ' , timestamp = False , terminal = True )
2018-02-04 01:11:35 +00:00
def showHelpSuggestion ( self ) :
2018-02-22 07:24:25 +00:00
'''
Displays a message suggesting help
'''
2019-07-21 00:29:08 +00:00
logger . info ( ' Do ' + logger . colors . bold + sys . argv [ 0 ] + ' --help ' + logger . colors . reset + logger . colors . fg . green + ' for Onionr help. ' , terminal = True )
2018-02-04 01:11:35 +00:00
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
'''
2019-05-07 17:56:20 +00:00
if config . get ( ' general.dev_mode ' , False ) :
override = True
2019-03-08 06:30:14 +00:00
commands . daemonlaunch . start ( self , input , override )
2018-02-22 07:24:25 +00:00
2019-01-08 05:51:39 +00:00
def setClientAPIInst ( self , inst ) :
self . clientAPIInst = inst
def getClientApi ( self ) :
while self . clientAPIInst == ' ' :
time . sleep ( 0.5 )
return self . clientAPIInst
2018-02-04 01:11:35 +00:00
2017-12-26 07:25:29 +00:00
def daemon ( self ) :
2018-02-22 07:24:25 +00:00
'''
Starts the Onionr communication daemon
'''
2019-03-08 01:08:06 +00:00
commands . daemonlaunch . daemon ( self )
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
'''
2019-03-08 01:08:06 +00:00
commands . daemonlaunch . kill_daemon ( self )
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
'''
2019-03-08 01:08:06 +00:00
commands . onionrstatistics . show_stats ( self )
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
'''
2019-03-08 06:30:14 +00:00
commands . show_help ( self , command )
2018-02-04 01:11:35 +00:00
2018-09-04 18:56:05 +00:00
def getFile ( self ) :
'''
Get a file from onionr blocks
'''
2019-03-08 06:30:14 +00:00
commands . filecommands . getFile ( self )
2018-09-04 18:56:05 +00:00
2018-12-09 17:29:39 +00:00
def addWebpage ( self ) :
'''
Add a webpage to the onionr network
'''
self . addFile ( singleBlock = True , blockType = ' html ' )
2019-02-07 01:03:31 +00:00
def addFile ( self , singleBlock = False , blockType = ' bin ' ) :
2018-06-01 04:25:28 +00:00
'''
Adds a file to the onionr network
'''
2019-03-08 01:08:06 +00:00
commands . filecommands . add_file ( self , singleBlock , blockType )
2018-04-23 03:45:25 +00:00
2018-07-06 04:27:12 +00:00
if __name__ == " __main__ " :
Onionr ( )