diff --git a/.gitignore b/.gitignore
index 724137bc..4f406e73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@ __pycache__/
onionr/data/config.ini
onionr/data/*.db
onionr/data-old/*
+onionr/*.pyc
+onionr/*.log
diff --git a/onionr/api.py b/onionr/api.py
index 37721490..bc3c069e 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, gnupg, os
+import configparser, sys, random, threading, hmac, hashlib, base64, time, math, gnupg, os, logging
from core import Core
import onionrutils
@@ -42,10 +42,13 @@ class API:
This also saves the used host (random localhost IP address) to the data folder in host.txt
'''
if os.path.exists('dev-enabled'):
- print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
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
@@ -55,13 +58,13 @@ class API:
bindPort = int(self.config['CLIENT']['PORT'])
self.bindPort = bindPort
self.clientToken = self.config['CLIENT']['CLIENT HMAC']
- print(self.clientToken)
+ logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
if not debug and not self._developmentMode:
hostNums = [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)]
self.host = '127.' + str(hostNums[0]) + '.' + str(hostNums[1]) + '.' + str(hostNums[2])
else:
- self.host = '127.0.0.1'
+ self.host = '127.0.0.1'
hostFile = open('data/host.txt', 'w')
hostFile.write(self.host)
hostFile.close()
@@ -80,11 +83,11 @@ class API:
resp.headers['Access-Control-Allow-Origin'] = '*'
else:
resp.headers['server'] = 'Onionr'
- resp.headers['content-type'] = 'text/plain'
+ resp.headers['Content-Type'] = 'text/plain'
resp.headers["Content-Security-Policy"] = "default-src 'none'"
- resp.headers['x-frame-options'] = 'deny'
+ resp.headers['X-Frame-Options'] = 'deny'
return resp
-
+
@app.route('/client/')
def private_handler():
startTime = math.floor(time.time())
@@ -101,7 +104,7 @@ class API:
request.environ.get('werkzeug.server.shutdown')()
resp = Response('Goodbye')
elif action == 'stats':
- resp = Response('something')
+ resp = Response('me_irl')
elif action == 'init':
# generate PGP key
self._core.generateMainPGP()
@@ -156,17 +159,17 @@ class API:
resp = Response("Invalid request")
return resp
- print('Starting client on ' + self.host + ':' + str(bindPort))
- print('Client token:', self.clientToken)
+ logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
+ logger.debug('Client token: ' + logger.colors.underline + self.clientToken)
app.run(host=self.host, port=bindPort, debug=True, threaded=True)
-
+
def validateHost(self, hostType):
''' Validate various features of the request including:
If private (/client/), is the host header local?
If public (/public/), is the host header onion or i2p?
-
- Was x-request-with used?
+
+ Was X-Request-With used?
'''
if self.debug:
return
@@ -181,7 +184,8 @@ class API:
# Validate x-requested-with, to protect against CSRF/metadata leaks
if not self._developmentMode:
try:
- request.headers['x-requested-with']
+ request.headers['X-Requested-With']
except:
# we exit rather than abort to avoid fingerprinting
- sys.exit(1)
\ No newline at end of file
+ logger.debug('Avoiding fingerprinting, exiting...')
+ sys.exit(1)
diff --git a/onionr/communicator.py b/onionr/communicator.py
index ae808067..febbd103 100755
--- a/onionr/communicator.py
+++ b/onionr/communicator.py
@@ -19,7 +19,7 @@ and code to operate as a daemon, getting commands from the command queue databas
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import sqlite3, requests, hmac, hashlib, time, sys, os
+import sqlite3, requests, hmac, hashlib, time, sys, os, logger
import core
class OnionrCommunicate:
def __init__(self, debug, developmentMode):
@@ -30,8 +30,7 @@ class OnionrCommunicate:
self._core = core.Core()
blockProcessTimer = 0
blockProcessAmount = 5
- if debug:
- print('Communicator debugging enabled')
+ logger.debug('Communicator debugging enabled.')
torID = open('data/hs/hostname').read()
# get our own PGP fingerprint
@@ -40,7 +39,7 @@ class OnionrCommunicate:
self._core.generateMainPGP(torID)
with open(fingerprintFile,'r') as f:
self.pgpOwnFingerprint = f.read()
- print('My PGP fingerprint is ' + self.pgpOwnFingerprint)
+ logger.info('My PGP fingerprint is ' + logger.colors.underline + self.pgpOwnFingerprint + logger.colors.reset + logger.colors.fg.green + '.')
while True:
command = self._core.daemonQueue()
@@ -51,11 +50,10 @@ class OnionrCommunicate:
self.lookupBlocks()
self._core.processBlocks()
blockProcessTimer = 0
- if debug:
- print('Communicator daemon heartbeat')
+ logger.debug('Communicator daemon heartbeat')
if command != False:
if command[0] == 'shutdown':
- print('Daemon recieved exit command.')
+ logger.warn('Daemon recieved exit command.')
break
time.sleep(1)
return
@@ -94,11 +92,11 @@ class OnionrCommunicate:
# skip hash if it isn't valid
continue
else:
- print('adding', i, 'to hash database')
+ logger.debug('Adding ' + i + ' to hash database...')
self._core.addToBlockDB(i)
return
-
+
def performGet(self, action, peer, data=None, type='tor'):
'''performs a request to a peer through Tor or i2p (currently only tor)'''
if not peer.endswith('.onion') and not peer.endswith('.onion/'):
@@ -114,7 +112,7 @@ class OnionrCommunicate:
except requests.exceptions.RequestException:
return False
return r.text
-
+
shouldRun = False
debug = False
diff --git a/onionr/core.py b/onionr/core.py
index b5507511..4943b5da 100644
--- a/onionr/core.py
+++ b/onionr/core.py
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import sqlite3, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl
+import sqlite3, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger
from Crypto.Cipher import AES
from Crypto import Random
import netcontroller
@@ -26,7 +26,7 @@ if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
- sys.stderr.write('On Python 3 versions prior to 3.6.x, you need the sha3 module')
+ logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
class Core:
@@ -66,7 +66,7 @@ class Core:
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
t = (peerID, name, 'unknown')
- c.execute('Insert into peers (id, name, dateSeen) values(?, ?, ?);', t)
+ c.execute('insert into peers (id, name, dateSeen) values(?, ?, ?);', t)
conn.commit()
conn.close()
return True
@@ -295,4 +295,3 @@ class Core:
for i in row:
retData += i
return retData
-
\ No newline at end of file
diff --git a/onionr/logger.py b/onionr/logger.py
new file mode 100644
index 00000000..fad5e95e
--- /dev/null
+++ b/onionr/logger.py
@@ -0,0 +1,153 @@
+'''
+ Onionr - P2P Microblogging Platform & Social network
+
+ This file handles all operations involving logging
+'''
+'''
+ 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 re
+
+class colors:
+ '''
+ This class allows you to set the color if ANSI codes are supported
+ '''
+ reset='\033[0m'
+ bold='\033[01m'
+ disable='\033[02m'
+ underline='\033[04m'
+ reverse='\033[07m'
+ strikethrough='\033[09m'
+ invisible='\033[08m'
+ italics='\033[3m'
+ class fg:
+ black='\033[30m'
+ red='\033[31m'
+ green='\033[32m'
+ orange='\033[33m'
+ blue='\033[34m'
+ purple='\033[35m'
+ cyan='\033[36m'
+ lightgrey='\033[37m'
+ darkgrey='\033[90m'
+ lightred='\033[91m'
+ lightgreen='\033[92m'
+ yellow='\033[93m'
+ lightblue='\033[94m'
+ pink='\033[95m'
+ lightcyan='\033[96m'
+ class bg:
+ black='\033[40m'
+ red='\033[41m'
+ green='\033[42m'
+ orange='\033[43m'
+ blue='\033[44m'
+ purple='\033[45m'
+ cyan='\033[46m'
+ lightgrey='\033[47m'
+ @staticmethod
+ def filter(data):
+ return re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]').sub('', str(data))
+
+'''
+ Use the bitwise operators to merge these settings
+'''
+USE_ANSI = 0b100
+OUTPUT_TO_CONSOLE = 0b010
+OUTPUT_TO_FILE = 0b001
+
+LEVEL_DEBUG = 1
+LEVEL_INFO = 2
+LEVEL_WARN = 3
+LEVEL_ERROR = 4
+LEVEL_FATAL = 5
+
+_type = OUTPUT_TO_CONSOLE | USE_ANSI # the default settings for logging
+_level = LEVEL_DEBUG # the lowest level to log
+_outputfile = './output.log' # the file to log to
+
+'''
+ Set the settings for the logger using bitwise operators
+'''
+def set_settings(type):
+ global _type
+ _type = type
+
+'''
+ Get settings from the logger
+'''
+def get_settings():
+ return _type
+
+'''
+ Set the lowest log level to output
+'''
+def set_level(level):
+ global _level
+ _level = level
+
+'''
+ Get the lowest log level currently being outputted
+'''
+def get_level():
+ return _level
+
+'''
+ Outputs raw data to console without formatting
+'''
+def raw(data):
+ if get_settings() & OUTPUT_TO_CONSOLE:
+ print(data)
+ if get_settings() & OUTPUT_TO_FILE:
+ with open(_outputfile, "a+") as f:
+ f.write(colors.filter(data) + '\n')
+
+'''
+ Logs the data
+ prefix : The prefix to the output
+ data : The actual data to output
+ color : The color to output before the data
+'''
+def log(prefix, data, color = ''):
+ output = colors.reset + str(color) + '[' + colors.bold + str(prefix) + colors.reset + str(color) + '] ' + str(data) + colors.reset
+ if not get_settings() & USE_ANSI:
+ output = colors.filter(output)
+
+ raw(output)
+
+# debug: when there is info that could be useful for debugging purposes only
+def debug(data):
+ if get_level() <= LEVEL_DEBUG:
+ log('/', data)
+
+# info: when there is something to notify the user of, such as the success of a process
+def info(data):
+ if get_level() <= LEVEL_INFO:
+ log('+', data, colors.fg.green)
+
+# warn: when there is a potential for something bad to happen
+def warn(data):
+ if get_level() <= LEVEL_WARN:
+ log('!', data, colors.fg.orange)
+
+# error: when only one function, module, or process of the program encountered a problem and must stop
+def error(data):
+ if get_level() <= LEVEL_ERROR:
+ log('-', data, colors.fg.red)
+
+# fatal: when the something so bad has happened that the prorgam must stop
+def fatal(data):
+ if get_level() <= LEVEL_FATAL:
+ log('#', data, colors.bg.red + colors.fg.black)
diff --git a/onionr/netcontroller.py b/onionr/netcontroller.py
index 7607bcba..ca229826 100644
--- a/onionr/netcontroller.py
+++ b/onionr/netcontroller.py
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import subprocess, os, random, sys
+import subprocess, os, random, sys, logger
class NetController:
'''NetController
This class handles hidden service setup on Tor and I2P
@@ -56,10 +56,10 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
if 'Bootstrapped 100%: Done' in line.decode():
break
elif 'Opening Socks listener' in line.decode():
- print(line.decode())
- print('Finished starting Tor')
+ logger.debug(line.decode())
+ logger.info('Finished starting Tor')
self.readyState = True
myID = open('data/hs/hostname', 'r')
self.myID = myID.read()
myID.close()
- return
\ No newline at end of file
+ return
diff --git a/onionr/onionr.py b/onionr/onionr.py
index 51dfd24e..af4f975c 100755
--- a/onionr/onionr.py
+++ b/onionr/onionr.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
'''
- Onionr - P2P Microblogging Platform & Social network.
+ Onionr - P2P Microblogging Platform & Social network.
Onionr is the name for both the protocol and the original/reference software.
@@ -20,10 +20,9 @@
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
-import gui, api, colors, core
+import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger
+import gui, api, core
from onionrutils import OnionrUtils
-from colors import Colors
from netcontroller import NetController
class Onionr:
@@ -37,18 +36,18 @@ class Onionr:
except FileNotFoundError:
pass
if os.path.exists('dev-enabled'):
- print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
self._developmentMode = True
+ logger.set_level(logger.LEVEL_DEBUG)
+ logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
-
- colors = Colors()
+ logger.set_level(logger.LEVEL_INFO)
self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils(self.onionrCore)
# Get configuration and Handle commands
-
+
self.debug = False # Whole application debugging
if os.path.exists('data-encrypted.dat'):
@@ -59,12 +58,12 @@ class Onionr:
if os.path.exists('data/'):
break
else:
- print('Failed to decrypt: ' + result[1])
+ logger.error('Failed to decrypt: ' + result[1])
else:
if not os.path.exists('data/'):
os.mkdir('data/')
os.mkdir('data/blocks/')
-
+
if not os.path.exists('data/peers.db'):
self.onionrCore.createPeerDB()
pass
@@ -94,7 +93,7 @@ class Onionr:
finally:
if command == 'start':
if os.path.exists('.onionr-lock'):
- self.onionrUtils.printErr('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).')
+ 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')
@@ -110,32 +109,31 @@ class Onionr:
elif command == 'help' or command == '--help':
self.showHelp()
elif command == '':
- print('Do', sys.argv[0], ' --help for Onionr help.')
+ logger.info('Do ' + logger.colors.bold + sys.argv[0] + ' --help' + logger.colors.reset + logger.colors.fg.green + ' for Onionr help.')
else:
- print(colors.RED, 'Invalid Command', colors.RESET)
-
+ logger.error('Invalid command.')
+
if not self._developmentMode:
- encryptionPassword = self.onionrUtils.getPassword('Enter password to encrypt directory.')
+ encryptionPassword = self.onionrUtils.getPassword('Enter password to encrypt directory: ')
self.onionrCore.dataDirEncrypt(encryptionPassword)
shutil.rmtree('data/')
return
def daemon(self):
''' Start the Onionr communication daemon
'''
- colors = Colors()
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
net = NetController(self.config['CLIENT']['PORT'])
- print('Tor is starting...')
+ logger.info('Tor is starting...')
net.startTor()
- print(colors.GREEN + 'Started Tor .onion service: ' + colors.UNDERLINE + net.myID + colors.RESET)
+ logger.info('Started Tor .onion service: ' + logger.colors.underline + net.myID)
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
- print('Started communicator')
+ logger.debug('Started communicator')
api.API(self.config, self.debug)
return
def killDaemon(self):
'''Shutdown the Onionr Daemon'''
- print('Killing the running daemon')
+ logger.warn('Killing the running daemon')
try:
self.onionrUtils.localCommand('shutdown')
except requests.exceptions.ConnectionError:
@@ -149,6 +147,6 @@ class Onionr:
def showHelp(self):
'''Show help for Onionr'''
return
-
-Onionr()
\ No newline at end of file
+
+Onionr()
diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py
index 85e33780..a1213341 100644
--- a/onionr/onionrutils.py
+++ b/onionr/onionrutils.py
@@ -18,12 +18,12 @@
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, gnupg, hashlib
+import getpass, sys, requests, configparser, os, socket, gnupg, hashlib, logger
if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
- sys.stderr.write('On Python 3 versions prior to 3.6.x, you need the sha3 module')
+ logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
class OnionrUtils:
'''Various useful functions'''
@@ -33,7 +33,7 @@ class OnionrUtils:
return
def printErr(self, text='an error occured'):
'''Print an error message to stderr with a new line'''
- sys.stderr.write(text + '\n')
+ logger.error(text)
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()
@@ -51,7 +51,7 @@ class OnionrUtils:
print('Confirm password: ')
pass2 = getpass.getpass()
if pass1 != pass2:
- print("Passwords do not match.")
+ logger.error("Passwords do not match.")
input()
else:
break
@@ -101,4 +101,4 @@ class OnionrUtils:
int(data, 16)
except ValueError:
retVal = False
- return retVal
\ No newline at end of file
+ return retVal
diff --git a/onionr/tests.py b/onionr/tests.py
index 60c28de3..93c5ba38 100755
--- a/onionr/tests.py
+++ b/onionr/tests.py
@@ -14,18 +14,18 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import unittest, sys, os, base64, tarfile, shutil, simplecrypt
+import unittest, sys, os, base64, tarfile, shutil, simplecrypt, logger
class OnionrTests(unittest.TestCase):
def testPython3(self):
if sys.version_info.major != 3:
- print(sys.version_info.major)
+ logger.debug('Python version: ' + sys.version_info.major)
self.assertTrue(False)
else:
self.assertTrue(True)
def testNone(self):
- print('--------------------------')
- print('Running simple program run test')
+ logger.debug('--------------------------')
+ logger.info('Running simple program run test...')
# Test just running ./onionr with no arguments
blank = os.system('./onionr.py')
if blank != 0:
@@ -33,8 +33,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(True)
def testPeer_a_DBCreation(self):
- print('--------------------------')
- print('Running peer db creation test')
+ logger.debug('--------------------------')
+ logger.info('Running peer db creation test...')
if os.path.exists('data/peers.db'):
os.remove('data/peers.db')
import core
@@ -45,8 +45,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testPeer_b_addPeerToDB(self):
- print('--------------------------')
- print('Running peer db insertion test')
+ logger.debug('--------------------------')
+ logger.info('Running peer db insertion test...')
import core
myCore = core.Core()
if not os.path.exists('data/peers.db'):
@@ -58,8 +58,8 @@ class OnionrTests(unittest.TestCase):
def testData_b_Encrypt(self):
self.assertTrue(True)
return
- print('--------------------------')
- print('Running data dir encrypt test')
+ logger.debug('--------------------------')
+ logger.info('Running data dir encrypt test...')
import core
myCore = core.Core()
myCore.dataDirEncrypt('password')
@@ -70,8 +70,8 @@ class OnionrTests(unittest.TestCase):
def testData_a_Decrypt(self):
self.assertTrue(True)
return
- print('--------------------------')
- print('Running data dir decrypt test')
+ logger.debug('--------------------------')
+ logger.info('Running data dir decrypt test...')
import core
myCore = core.Core()
myCore.dataDirDecrypt('password')
@@ -80,8 +80,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testPGPGen(self):
- print('--------------------------')
- print('Testing PGP key generation')
+ logger.debug('--------------------------')
+ logger.info('Running PGP key generation test...')
if os.path.exists('data/pgp/'):
self.assertTrue(True)
else:
@@ -94,8 +94,8 @@ class OnionrTests(unittest.TestCase):
if os.path.exists('data/pgp/'):
self.assertTrue(True)
def testHMACGen(self):
- print('--------------------------')
- print('running daemon queue test')
+ logger.debug('--------------------------')
+ logger.info('Running HMAC generation test...')
# Test if hmac key generation is working
import core
myCore = core.Core()
@@ -105,8 +105,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testQueue(self):
- print('--------------------------')
- print('running daemon queue test')
+ logger.debug('--------------------------')
+ logger.info('Running daemon queue test...')
# test if the daemon queue can read/write data
import core
myCore = core.Core()