Merge branch 'master' of github.com:Atheros1/PyBitmessage
This commit is contained in:
commit
c6291f55ef
|
@ -720,7 +720,6 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
helper_bootstrap.knownNodes()
|
helper_bootstrap.knownNodes()
|
||||||
helper_bootstrap.dns()
|
helper_bootstrap.dns()
|
||||||
|
|
||||||
# Start the address generation thread
|
# Start the address generation thread
|
||||||
addressGeneratorThread = addressGenerator()
|
addressGeneratorThread = addressGenerator()
|
||||||
addressGeneratorThread.daemon = True # close the main program even if there are threads left
|
addressGeneratorThread.daemon = True # close the main program even if there are threads left
|
||||||
|
|
|
@ -29,6 +29,8 @@ import os
|
||||||
from pyelliptic.openssl import OpenSSL
|
from pyelliptic.openssl import OpenSSL
|
||||||
import pickle
|
import pickle
|
||||||
import platform
|
import platform
|
||||||
|
import debug
|
||||||
|
from debug import logger
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
@ -1876,7 +1878,14 @@ class MyForm(QtGui.QMainWindow):
|
||||||
shared.knownNodesLock.release()
|
shared.knownNodesLock.release()
|
||||||
os.remove(shared.appdata + 'keys.dat')
|
os.remove(shared.appdata + 'keys.dat')
|
||||||
os.remove(shared.appdata + 'knownnodes.dat')
|
os.remove(shared.appdata + 'knownnodes.dat')
|
||||||
|
previousAppdataLocation = shared.appdata
|
||||||
shared.appdata = ''
|
shared.appdata = ''
|
||||||
|
debug.restartLoggingInUpdatedAppdataLocation()
|
||||||
|
try:
|
||||||
|
os.remove(previousAppdataLocation + 'debug.log')
|
||||||
|
os.remove(previousAppdataLocation + 'debug.log.1')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if shared.appdata == '' and not self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we ARE using portable mode now but the user selected that we shouldn't...
|
if shared.appdata == '' and not self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we ARE using portable mode now but the user selected that we shouldn't...
|
||||||
shared.appdata = shared.lookupAppdataFolder()
|
shared.appdata = shared.lookupAppdataFolder()
|
||||||
|
@ -1896,6 +1905,12 @@ class MyForm(QtGui.QMainWindow):
|
||||||
shared.knownNodesLock.release()
|
shared.knownNodesLock.release()
|
||||||
os.remove('keys.dat')
|
os.remove('keys.dat')
|
||||||
os.remove('knownnodes.dat')
|
os.remove('knownnodes.dat')
|
||||||
|
debug.restartLoggingInUpdatedAppdataLocation()
|
||||||
|
try:
|
||||||
|
os.remove('debug.log')
|
||||||
|
os.remove('debug.log.1')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def click_radioButtonBlacklist(self):
|
def click_radioButtonBlacklist(self):
|
||||||
if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white':
|
if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white':
|
||||||
|
|
|
@ -5,6 +5,7 @@ import time
|
||||||
import shutil # used for moving the messages.dat file
|
import shutil # used for moving the messages.dat file
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
from debug import logger
|
||||||
|
|
||||||
# This thread exists because SQLITE3 is so un-threadsafe that we must
|
# This thread exists because SQLITE3 is so un-threadsafe that we must
|
||||||
# submit queries to it and it puts results back in a different queue. They
|
# submit queries to it and it puts results back in a different queue. They
|
||||||
|
|
85
src/debug.py
85
src/debug.py
|
@ -23,48 +23,59 @@ import shared
|
||||||
# TODO(xj9): Get from a config file.
|
# TODO(xj9): Get from a config file.
|
||||||
log_level = 'DEBUG'
|
log_level = 'DEBUG'
|
||||||
|
|
||||||
logging.config.dictConfig({
|
def configureLogging():
|
||||||
'version': 1,
|
logging.config.dictConfig({
|
||||||
'formatters': {
|
'version': 1,
|
||||||
'default': {
|
'formatters': {
|
||||||
'format': '%(asctime)s - %(levelname)s - %(message)s',
|
'default': {
|
||||||
|
'format': '%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
'handlers': {
|
||||||
'handlers': {
|
'console': {
|
||||||
'console': {
|
'class': 'logging.StreamHandler',
|
||||||
'class': 'logging.StreamHandler',
|
'formatter': 'default',
|
||||||
'formatter': 'default',
|
'level': log_level,
|
||||||
'level': log_level,
|
'stream': 'ext://sys.stdout'
|
||||||
'stream': 'ext://sys.stdout'
|
},
|
||||||
|
'file': {
|
||||||
|
'class': 'logging.handlers.RotatingFileHandler',
|
||||||
|
'formatter': 'default',
|
||||||
|
'level': log_level,
|
||||||
|
'filename': shared.appdata + 'debug.log',
|
||||||
|
'maxBytes': 2097152, # 2 MiB
|
||||||
|
'backupCount': 1,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'file': {
|
'loggers': {
|
||||||
'class': 'logging.handlers.RotatingFileHandler',
|
'console_only': {
|
||||||
'formatter': 'default',
|
'handlers': ['console'],
|
||||||
|
'propagate' : 0
|
||||||
|
},
|
||||||
|
'file_only': {
|
||||||
|
'handlers': ['file'],
|
||||||
|
'propagate' : 0
|
||||||
|
},
|
||||||
|
'both': {
|
||||||
|
'handlers': ['console', 'file'],
|
||||||
|
'propagate' : 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'root': {
|
||||||
'level': log_level,
|
'level': log_level,
|
||||||
'filename': shared.appdata + 'debug.log',
|
|
||||||
'maxBytes': 2097152, # 2 MiB
|
|
||||||
'backupCount': 1,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'loggers': {
|
|
||||||
'console_only': {
|
|
||||||
'handlers': ['console'],
|
'handlers': ['console'],
|
||||||
'propagate' : 0
|
|
||||||
},
|
},
|
||||||
'file_only': {
|
})
|
||||||
'handlers': ['file'],
|
|
||||||
'propagate' : 0
|
|
||||||
},
|
|
||||||
'both': {
|
|
||||||
'handlers': ['console', 'file'],
|
|
||||||
'propagate' : 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'root': {
|
|
||||||
'level': log_level,
|
|
||||||
'handlers': ['console'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
# TODO (xj9): Get from a config file.
|
# TODO (xj9): Get from a config file.
|
||||||
#logger = logging.getLogger('console_only')
|
#logger = logging.getLogger('console_only')
|
||||||
|
configureLogging()
|
||||||
logger = logging.getLogger('both')
|
logger = logging.getLogger('both')
|
||||||
|
|
||||||
|
def restartLoggingInUpdatedAppdataLocation():
|
||||||
|
global logger
|
||||||
|
for i in list(logger.handlers):
|
||||||
|
logger.removeHandler(i)
|
||||||
|
i.flush()
|
||||||
|
i.close()
|
||||||
|
configureLogging()
|
||||||
|
logger = logging.getLogger('both')
|
|
@ -33,7 +33,7 @@ def dns():
|
||||||
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
||||||
shared.knownNodes[1][item[4][0]] = (8080, int(time.time()))
|
shared.knownNodes[1][item[4][0]] = (8080, int(time.time()))
|
||||||
except:
|
except:
|
||||||
print 'bootstrap8080.bitmessage.org DNS bootstraping failed.'
|
print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.'
|
||||||
try:
|
try:
|
||||||
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80):
|
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80):
|
||||||
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
||||||
|
|
|
@ -76,6 +76,8 @@ def loadConfig():
|
||||||
print 'Creating new config files in', shared.appdata
|
print 'Creating new config files in', shared.appdata
|
||||||
if not os.path.exists(shared.appdata):
|
if not os.path.exists(shared.appdata):
|
||||||
os.makedirs(shared.appdata)
|
os.makedirs(shared.appdata)
|
||||||
|
if not sys.platform.startswith('win'):
|
||||||
|
os.umask(0o077)
|
||||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||||
shared.config.write(configfile)
|
shared.config.write(configfile)
|
||||||
|
|
||||||
|
|
104
src/shared.py
104
src/shared.py
|
@ -8,22 +8,26 @@ maximumAgeOfNodesThatIAdvertiseToOthers = 10800 # Equals three hours
|
||||||
useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the normal network, you won't be able to send or sometimes receive messages.
|
useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the normal network, you won't be able to send or sometimes receive messages.
|
||||||
|
|
||||||
|
|
||||||
import threading
|
# Libraries.
|
||||||
import sys
|
|
||||||
from addresses import *
|
|
||||||
import highlevelcrypto
|
|
||||||
import Queue
|
|
||||||
import pickle
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import socket
|
import os
|
||||||
|
import pickle
|
||||||
|
import Queue
|
||||||
import random
|
import random
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import stat
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Project imports.
|
||||||
|
from addresses import *
|
||||||
import highlevelcrypto
|
import highlevelcrypto
|
||||||
import shared
|
import shared
|
||||||
import helper_startup
|
import helper_startup
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config = ConfigParser.SafeConfigParser()
|
config = ConfigParser.SafeConfigParser()
|
||||||
myECCryptorObjects = {}
|
myECCryptorObjects = {}
|
||||||
MyECSubscriptionCryptorObjects = {}
|
MyECSubscriptionCryptorObjects = {}
|
||||||
|
@ -118,8 +122,11 @@ def lookupAppdataFolder():
|
||||||
if "HOME" in environ:
|
if "HOME" in environ:
|
||||||
dataFolder = path.join(os.environ["HOME"], "Library/Application Support/", APPNAME) + '/'
|
dataFolder = path.join(os.environ["HOME"], "Library/Application Support/", APPNAME) + '/'
|
||||||
else:
|
else:
|
||||||
logger.critical('Could not find home folder, please report this message and your '
|
stringToLog = 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.'
|
||||||
'OS X version to the BitMessage Github.')
|
if 'logger' in globals():
|
||||||
|
logger.critical(stringToLog)
|
||||||
|
else:
|
||||||
|
print stringToLog
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
elif 'win32' in sys.platform or 'win64' in sys.platform:
|
elif 'win32' in sys.platform or 'win64' in sys.platform:
|
||||||
|
@ -133,9 +140,14 @@ def lookupAppdataFolder():
|
||||||
|
|
||||||
# Migrate existing data to the proper location if this is an existing install
|
# Migrate existing data to the proper location if this is an existing install
|
||||||
try:
|
try:
|
||||||
logger.info("Moving data folder to %s" % (dataFolder))
|
|
||||||
move(path.join(environ["HOME"], ".%s" % APPNAME), dataFolder)
|
move(path.join(environ["HOME"], ".%s" % APPNAME), dataFolder)
|
||||||
|
stringToLog = "Moving data folder to %s" % (dataFolder)
|
||||||
|
if 'logger' in globals():
|
||||||
|
logger.info(stringToLog)
|
||||||
|
else:
|
||||||
|
print stringToLog
|
||||||
except IOError:
|
except IOError:
|
||||||
|
# Old directory may not exist.
|
||||||
pass
|
pass
|
||||||
dataFolder = dataFolder + '/'
|
dataFolder = dataFolder + '/'
|
||||||
return dataFolder
|
return dataFolder
|
||||||
|
@ -181,23 +193,26 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def safeConfigGetBoolean(section,field):
|
def safeConfigGetBoolean(section,field):
|
||||||
try:
|
try:
|
||||||
return config.getboolean(section,field)
|
return config.getboolean(section,field)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def decodeWalletImportFormat(WIFstring):
|
def decodeWalletImportFormat(WIFstring):
|
||||||
fullString = arithmetic.changebase(WIFstring,58,256)
|
fullString = arithmetic.changebase(WIFstring,58,256)
|
||||||
privkey = fullString[:-4]
|
privkey = fullString[:-4]
|
||||||
if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
|
if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
|
||||||
sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum failed. Here is the PRIVATE key: %s\n' % str(WIFstring))
|
logger.error('Major problem! When trying to decode one of your private keys, the checksum '
|
||||||
|
'failed. Here is the PRIVATE key: %s\n' % str(WIFstring))
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
#checksum passed
|
#checksum passed
|
||||||
if privkey[0] == '\x80':
|
if privkey[0] == '\x80':
|
||||||
return privkey[1:]
|
return privkey[1:]
|
||||||
else:
|
else:
|
||||||
sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum passed but the key doesn\'t begin with hex 80. Here is the PRIVATE key: %s\n' % str(WIFstring))
|
logger.error('Major problem! When trying to decode one of your private keys, the '
|
||||||
|
'checksum passed but the key doesn\'t begin with hex 80. Here is the '
|
||||||
|
'PRIVATE key: %s\n' % str(WIFstring))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,19 +221,32 @@ def reloadMyAddressHashes():
|
||||||
myECCryptorObjects.clear()
|
myECCryptorObjects.clear()
|
||||||
myAddressesByHash.clear()
|
myAddressesByHash.clear()
|
||||||
#myPrivateKeys.clear()
|
#myPrivateKeys.clear()
|
||||||
|
|
||||||
|
keyfileSecure = checkSensitiveFilePermissions(appdata + 'keys.dat')
|
||||||
configSections = config.sections()
|
configSections = config.sections()
|
||||||
|
hasEnabledKeys = False
|
||||||
for addressInKeysFile in configSections:
|
for addressInKeysFile in configSections:
|
||||||
if addressInKeysFile <> 'bitmessagesettings':
|
if addressInKeysFile <> 'bitmessagesettings':
|
||||||
isEnabled = config.getboolean(addressInKeysFile, 'enabled')
|
isEnabled = config.getboolean(addressInKeysFile, 'enabled')
|
||||||
if isEnabled:
|
if isEnabled:
|
||||||
|
hasEnabledKeys = True
|
||||||
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
|
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
|
||||||
if addressVersionNumber == 2 or addressVersionNumber == 3:
|
if addressVersionNumber == 2 or addressVersionNumber == 3:
|
||||||
privEncryptionKey = decodeWalletImportFormat(config.get(addressInKeysFile, 'privencryptionkey')).encode('hex') #returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error
|
# Returns a simple 32 bytes of information encoded in 64 Hex characters,
|
||||||
|
# or null if there was an error.
|
||||||
|
privEncryptionKey = decodeWalletImportFormat(
|
||||||
|
config.get(addressInKeysFile, 'privencryptionkey')).encode('hex')
|
||||||
|
|
||||||
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
|
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
|
||||||
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
|
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
|
||||||
myAddressesByHash[hash] = addressInKeysFile
|
myAddressesByHash[hash] = addressInKeysFile
|
||||||
|
|
||||||
else:
|
else:
|
||||||
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
|
logger.error('Error in reloadMyAddressHashes: Can\'t handle address '
|
||||||
|
'versions other than 2 or 3.\n')
|
||||||
|
|
||||||
|
if not keyfileSecure:
|
||||||
|
fixSensitiveFilePermissions(appdata + 'keys.dat', hasEnabledKeys)
|
||||||
|
|
||||||
def reloadBroadcastSendersForWhichImWatching():
|
def reloadBroadcastSendersForWhichImWatching():
|
||||||
logger.debug('reloading subscriptions...')
|
logger.debug('reloading subscriptions...')
|
||||||
|
@ -269,6 +297,7 @@ def doCleanShutdown():
|
||||||
sqlSubmitQueue.put('exit')
|
sqlSubmitQueue.put('exit')
|
||||||
sqlLock.release()
|
sqlLock.release()
|
||||||
logger.info('Finished flushing inventory.')
|
logger.info('Finished flushing inventory.')
|
||||||
|
|
||||||
# Wait long enough to guarantee that any running proof of work worker threads will check the
|
# Wait long enough to guarantee that any running proof of work worker threads will check the
|
||||||
# shutdown variable and exit. If the main thread closes before they do then they won't stop.
|
# shutdown variable and exit. If the main thread closes before they do then they won't stop.
|
||||||
time.sleep(.25)
|
time.sleep(.25)
|
||||||
|
@ -306,5 +335,40 @@ def fixPotentiallyInvalidUTF8Data(text):
|
||||||
output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text)
|
output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
# Checks sensitive file permissions for inappropriate umask during keys.dat creation.
|
||||||
|
# (Or unwise subsequent chmod.)
|
||||||
|
#
|
||||||
|
# Returns true iff file appears to have appropriate permissions.
|
||||||
|
def checkSensitiveFilePermissions(filename):
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# TODO: This might deserve extra checks by someone familiar with
|
||||||
|
# Windows systems.
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
present_permissions = os.stat(filename)[0]
|
||||||
|
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
||||||
|
return present_permissions & disallowed_permissions == 0
|
||||||
|
|
||||||
|
# Fixes permissions on a sensitive file.
|
||||||
|
def fixSensitiveFilePermissions(filename, hasEnabledKeys):
|
||||||
|
if hasEnabledKeys:
|
||||||
|
logger.warning('Keyfile had insecure permissions, and there were enabled keys. '
|
||||||
|
'The truly paranoid should stop using them immediately.')
|
||||||
|
else:
|
||||||
|
logger.warning('Keyfile had insecure permissions, but there were no enabled keys.')
|
||||||
|
try:
|
||||||
|
present_permissions = os.stat(filename)[0]
|
||||||
|
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
||||||
|
allowed_permissions = ((1<<32)-1) ^ disallowed_permissions
|
||||||
|
new_permissions = (
|
||||||
|
allowed_permissions & present_permissions)
|
||||||
|
os.chmod(filename, new_permissions)
|
||||||
|
|
||||||
|
logger.info('Keyfile permissions automatically fixed.')
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
logger.exception('Keyfile permissions could not be fixed.')
|
||||||
|
raise
|
||||||
|
|
||||||
helper_startup.loadConfig()
|
helper_startup.loadConfig()
|
||||||
from debug import logger
|
from debug import logger
|
Reference in New Issue
Block a user