diff --git a/onionr/api.py b/onionr/api.py
index df5d2236..4175ecdf 100755
--- a/onionr/api.py
+++ b/onionr/api.py
@@ -20,7 +20,7 @@
import flask
from flask import request, Response, abort
from multiprocessing import Process
-import configparser, sys, random, threading, hmac, hashlib, base64, time, math, os, logger
+import sys, random, threading, hmac, hashlib, base64, time, math, os, logger, config
from core import Core
import onionrutils, onionrcrypto
@@ -37,31 +37,32 @@ class API:
else:
return True
- def __init__(self, config, debug):
+ def __init__(self, debug):
'''
Initialize the api server, preping variables for later use
This initilization defines all of the API entry points and handlers for the endpoints and errors
This also saves the used host (random localhost IP address) to the data folder in host.txt
'''
- if os.path.exists('dev-enabled'):
+
+ config.reload()
+
+ if config.get('devmode', True):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
- #logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
logger.set_level(logger.LEVEL_INFO)
- self.config = config
self.debug = debug
self._privateDelayTime = 3
self._core = Core()
self._crypto = onionrcrypto.OnionrCrypto(self._core)
self._utils = onionrutils.OnionrUtils(self._core)
app = flask.Flask(__name__)
- bindPort = int(self.config['CLIENT']['PORT'])
+ bindPort = int(config.get('CLIENT')['PORT'])
self.bindPort = bindPort
- self.clientToken = self.config['CLIENT']['CLIENT HMAC']
+ self.clientToken = config.get('CLIENT')['CLIENT HMAC']
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
diff --git a/onionr/config.py b/onionr/config.py
new file mode 100644
index 00000000..81e8bbb0
--- /dev/null
+++ b/onionr/config.py
@@ -0,0 +1,107 @@
+'''
+ Onionr - P2P Microblogging Platform & Social network
+
+ This file deals with configuration management.
+'''
+'''
+ 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 .
+'''
+
+import os, json, logger
+
+_configfile = os.path.abspath('data/config.json')
+_config = {}
+
+def get(key, default = None):
+ '''
+ Gets the key from configuration, or returns `default`
+ '''
+ if key in get_config():
+ return get_config()[key]
+ return default
+
+def set(key, value = None, savefile = False):
+ '''
+ Sets the key in configuration to `value`
+ '''
+
+ global _config
+ _config[key] = value
+
+ if savefile:
+ save()
+
+def check():
+ '''
+ Checks if the configuration file exists, creates it if not
+ '''
+
+ try:
+ if not os.path.exists(os.path.dirname(get_config_file())):
+ os.path.mkdirs(os.path.dirname(get_config_file()))
+ if not os.path.isfile(get_config_file()):
+ open(get_config_file(), 'a', encoding="utf8").close()
+ save()
+ except:
+ logger.warn('Failed to check configuration file.')
+
+def save():
+ '''
+ Saves the configuration data to the configuration file
+ '''
+
+ check()
+ try:
+ with open(get_config_file(), 'w', encoding="utf8") as configfile:
+ json.dump(get_config(), configfile)
+ except:
+ logger.warn('Failed to write to configuration file.')
+
+def reload():
+ '''
+ Reloads the configuration data in memory from the file
+ '''
+
+ check()
+ try:
+ with open(get_config_file(), 'r', encoding="utf8") as configfile:
+ set_config(json.loads(configfile.read()))
+ except:
+ logger.warn('Failed to parse configuration file.')
+
+def get_config():
+ '''
+ Gets the entire configuration as an array
+ '''
+ return _config
+
+def set_config(config):
+ '''
+ Sets the configuration to the array in arguments
+ '''
+ global _config
+ _config = config
+
+def get_config_file():
+ '''
+ Returns the absolute path to the configuration file
+ '''
+ return _configfile
+
+def set_config_file(configfile):
+ '''
+ Sets the path to the configuration file
+ '''
+ global _configfile
+ _configfile = os.abs.abspath(configfile)
diff --git a/onionr/onionr.py b/onionr/onionr.py
index 3098fe82..ed260450 100755
--- a/onionr/onionr.py
+++ b/onionr/onionr.py
@@ -20,8 +20,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger, platform
-import api, core, gui
+import sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform
+import api, core, gui, config, logger
from onionrutils import OnionrUtils
from netcontroller import NetController
@@ -39,12 +39,19 @@ class Onionr:
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.
'''
+
try:
os.chdir(sys.path[0])
except FileNotFoundError:
pass
- if os.path.exists('dev-enabled'):
+ # Load global configuration data
+
+ exists = os.path.exists(config.get_config_file())
+ config.set_config({'devmode': True}) # this is the default config, it will be overwritten if a config file already exists. Else, it saves it
+ config.reload() # this will read the configuration file into memory
+
+ if config.get('devmode', True):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
else:
@@ -54,7 +61,7 @@ class Onionr:
self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils(self.onionrCore)
- # Get configuration and Handle commands
+ # Handle commands
self.debug = False # Whole application debugging
@@ -79,10 +86,8 @@ class Onionr:
self.onionrCore.createAddressDB()
# Get configuration
- self.config = configparser.ConfigParser()
- if os.path.exists('data/config.ini'):
- self.config.read('data/config.ini')
- else:
+
+ if not exists:
# Generate default config
# Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
if self.debug:
@@ -92,9 +97,7 @@ class Onionr:
randomPort = random.randint(1024, 65535)
if self.onionrUtils.checkPort(randomPort):
break
- self.config['CLIENT'] = {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}
- with open('data/config.ini', 'w') as configfile:
- self.config.write(configfile)
+ config.set('CLIENT', {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}, True)
command = ''
try:
@@ -271,7 +274,7 @@ class Onionr:
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
if self._developmentMode:
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
- net = NetController(self.config['CLIENT']['PORT'])
+ net = NetController(config.get('CLIENT')['PORT'])
logger.info('Tor is starting...')
if not net.startTor():
sys.exit(1)
@@ -280,7 +283,7 @@ class Onionr:
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
logger.debug('Started communicator')
- api.API(self.config, self.debug)
+ api.API(self.debug)
return
@@ -290,7 +293,7 @@ class Onionr:
'''
logger.warn('Killing the running daemon')
- net = NetController(self.config['CLIENT']['PORT'])
+ net = NetController(config.get('CLIENT')['PORT'])
try:
self.onionrUtils.localCommand('shutdown')
except requests.exceptions.ConnectionError:
diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py
index b000e799..35ba38e3 100644
--- a/onionr/onionrutils.py
+++ b/onionr/onionrutils.py
@@ -18,30 +18,34 @@
along with this program. If not, see .
'''
# Misc functions that do not fit in the main api, but are useful
-import getpass, sys, requests, configparser, os, socket, hashlib, logger, sqlite3
+import getpass, sys, requests, os, socket, hashlib, logger, sqlite3, config
import nacl.signing, nacl.encoding
+
if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
+
class OnionrUtils:
- '''Various useful functions'''
+ '''
+ Various useful function
+ '''
def __init__(self, coreInstance):
self.fingerprintFile = 'data/own-fingerprint.txt'
self._core = coreInstance
return
+
def localCommand(self, command):
'''
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
'''
- config = configparser.ConfigParser()
- if os.path.exists('data/config.ini'):
- config.read('data/config.ini')
- else:
- return
- requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC'])
+
+ config.reload()
+
+ # TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
+ requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('CLIENT')['PORT']) + '/client/?action=' + command + '&token=' + config.get('CLIENT')['CLIENT HMAC'])
return
@@ -141,7 +145,7 @@ class OnionrUtils:
retVal = False
return retVal
-
+
def validatePubKey(self, key):
'''Validate if a string is a valid base32 encoded Ed25519 key'''
retVal = False
@@ -195,5 +199,5 @@ class OnionrUtils:
retVal = False
if not idNoDomain.isalnum():
retVal = False
-
- return retVal
\ No newline at end of file
+
+ return retVal
diff --git a/requirements.txt b/requirements.txt
index 77e25537..7338d372 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,3 +5,4 @@ simple_crypt==4.1.7
urllib3==1.19.1
sha3==0.2.1
PySocks==1.6.8
+urllib3