From 3063c256d4dee650fe66a8a8b937ffce7de6b8f6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 3 Aug 2013 12:45:15 +0100 Subject: [PATCH 01/86] Maximum message length configurable within keys.dat --- src/class_receiveDataThread.py | 13 ++++++++++++- src/helper_startup.py | 1 + src/shared.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 2693b293..f1ba979d 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -46,6 +46,17 @@ class receiveDataThread(threading.Thread): self.PORT = port self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header + self.maxMessageLength = 180000000 # maximum length of a message in bytes, default 180MB + + # get the maximum message length from the settings + try: + maxMsgLen = shared.config.getint( + 'bitmessagesettings', 'maxmessagelength') + if maxMsgLen > 32768: # minimum of 32K + self.maxMessageLength = maxMsgLen + except Exception: + pass + self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} self.selfInitiatedConnections = selfInitiatedConnections shared.connectedHostsList[ @@ -140,7 +151,7 @@ class receiveDataThread(threading.Thread): shared.knownNodes[self.streamNumber][ self.HOST] = (self.PORT, int(time.time())) shared.knownNodesLock.release() - if self.payloadLength <= 180000000: # If the size of the message is greater than 180MB, ignore it. (I get memory errors when processing messages much larger than this though it is concievable that this value will have to be lowered if some systems are less tolarant of large messages.) + if self.payloadLength <= self.maxMessageLength: # If the size of the message is greater than the maximum, ignore it. remoteCommand = self.data[4:16] with shared.printLock: print 'remoteCommand', repr(remoteCommand.replace('\x00', '')), ' from', self.HOST diff --git a/src/helper_startup.py b/src/helper_startup.py index d60ad681..c36589d5 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -67,6 +67,7 @@ def loadConfig(): shared.config.set( 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'dontconnect', 'true') + shared.config.set('bitmessagesettings', 'maxMessageLength', '180000000') if storeConfigFilesInSameDirectoryAsProgramByDefault: # Just use the same directory as the program and forget about diff --git a/src/shared.py b/src/shared.py index 32370524..aed1971e 100644 --- a/src/shared.py +++ b/src/shared.py @@ -371,4 +371,4 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys): raise helper_startup.loadConfig() -from debug import logger \ No newline at end of file +from debug import logger From 0f357529edfc1ded9991bdacf33fe7d877beef41 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Tue, 6 Aug 2013 22:28:21 +0100 Subject: [PATCH 02/86] bitmessagemain, changing prints tologger functions --- src/bitmessagemain.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b156cd0d..126c502b 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -34,11 +34,13 @@ from class_addressGenerator import * # Helper Functions import helper_bootstrap +# Debug logger +from debug import logger + import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: - print "You should use python 2.7.5 or greater." - print "Your version: {0}.{1}.{2}".format(*sys.version_info) + logger.critical("You should use python 2.7.5 or greater. Your version: {0}.{1}.{2}".format(*sys.version_info)) sys.exit(0) def connectToStream(streamNumber): @@ -126,7 +128,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): else: return False else: - print 'Authentication failed because header lacks Authentication field' + logger.warn('Authentication failed because header lacks Authentication field') time.sleep(2) return False @@ -276,7 +278,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if numberOfAddresses > 999: return 'API Error 0005: You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.' shared.apiAddressGeneratorReturnQueue.queue.clear() - print 'Requesting that the addressGenerator create', numberOfAddresses, 'addresses.' + logger.debug('Requesting that the addressGenerator create', numberOfAddresses, 'addresses.') shared.addressGeneratorQueue.put( ('createDeterministicAddresses', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) @@ -302,7 +304,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if streamNumber != 1: return 'API Error 0003: The stream number must be 1. Others aren\'t supported.' shared.apiAddressGeneratorReturnQueue.queue.clear() - print 'Requesting that the addressGenerator create', numberOfAddresses, 'addresses.' + logger.debug('Requesting that the addressGenerator create', numberOfAddresses, 'addresses.') shared.addressGeneratorQueue.put( ('getDeterministicAddress', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) @@ -515,8 +517,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': - with shared.printLock: - print 'API Error 0007: Could not decode address:', toAddress, ':', status + logger.warn('API Error 0007: Could not decode address:', toAddress, ':', status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + toAddress @@ -532,8 +533,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - with shared.printLock: - print 'API Error 0007: Could not decode address:', fromAddress, ':', status + logger.warn('API Error 0007: Could not decode address:', fromAddress, ':', status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -597,8 +597,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - with shared.printLock: - print 'API Error 0007: Could not decode address:', fromAddress, ':', status + logger.warn('API Error 0007: Could not decode address:', fromAddress, ':', status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -668,8 +667,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) if status != 'success': - with shared.printLock: - print 'API Error 0007: Could not decode address:', address, ':', status + logger.warn('API Error 0007: Could not decode address:', address, ':', status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + address @@ -808,8 +806,7 @@ if __name__ == "__main__": except: apiNotifyPath = '' if apiNotifyPath != '': - with shared.printLock: - print 'Trying to call', apiNotifyPath + logger.debug('Trying to call', apiNotifyPath) call([apiNotifyPath, "startingUp"]) singleAPIThread = singleAPI() @@ -827,16 +824,15 @@ if __name__ == "__main__": try: from PyQt4 import QtCore, QtGui except Exception as err: - print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon' - print 'Error message:', err + logger.error('PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon') + logger.error('Error message: '+ err) os._exit(0) import bitmessageqt bitmessageqt.run() else: shared.config.remove_option('bitmessagesettings', 'dontconnect') - with shared.printLock: - print 'Running as a daemon. You can use Ctrl+C to exit.' + logger.info('Running as a daemon. You can use Ctrl+C to exit.') while True: time.sleep(20) From 26b82984a2098a75178eac9e34d3ff98ee6a2b3c Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Wed, 7 Aug 2013 21:12:32 +0100 Subject: [PATCH 03/86] Fixes to logger function calls --- src/bitmessagemain.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 126c502b..3a99fc19 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -40,7 +40,7 @@ from debug import logger import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: - logger.critical("You should use python 2.7.5 or greater. Your version: {0}.{1}.{2}".format(*sys.version_info)) + logger.critical("You should use python 2.7.5 or greater. Your version: %s", format(*sys.version_info)) sys.exit(0) def connectToStream(streamNumber): @@ -278,7 +278,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if numberOfAddresses > 999: return 'API Error 0005: You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.' shared.apiAddressGeneratorReturnQueue.queue.clear() - logger.debug('Requesting that the addressGenerator create', numberOfAddresses, 'addresses.') + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) shared.addressGeneratorQueue.put( ('createDeterministicAddresses', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) @@ -304,7 +304,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if streamNumber != 1: return 'API Error 0003: The stream number must be 1. Others aren\'t supported.' shared.apiAddressGeneratorReturnQueue.queue.clear() - logger.debug('Requesting that the addressGenerator create', numberOfAddresses, 'addresses.') + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) shared.addressGeneratorQueue.put( ('getDeterministicAddress', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) @@ -517,7 +517,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address:', toAddress, ':', status) + logger.warn('API Error 0007: Could not decode address: %s : %s', toAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + toAddress @@ -533,7 +533,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address:', fromAddress, ':', status) + logger.warn('API Error 0007: Could not decode address: %s : %s', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -597,7 +597,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address:', fromAddress, ':', status) + logger.warn('API Error 0007: Could not decode address: %s : %s', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -667,7 +667,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) if status != 'success': - logger.warn('API Error 0007: Could not decode address:', address, ':', status) + logger.warn('API Error 0007: Could not decode address: %s : %s', address, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + address @@ -806,7 +806,7 @@ if __name__ == "__main__": except: apiNotifyPath = '' if apiNotifyPath != '': - logger.debug('Trying to call', apiNotifyPath) + logger.debug('Trying to call %s', apiNotifyPath) call([apiNotifyPath, "startingUp"]) singleAPIThread = singleAPI() @@ -825,7 +825,7 @@ if __name__ == "__main__": from PyQt4 import QtCore, QtGui except Exception as err: logger.error('PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon') - logger.error('Error message: '+ err) + logger.error('Error message: %s', err) os._exit(0) import bitmessageqt From b529280160680d73056e4141231d04e4232dae23 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Wed, 7 Aug 2013 21:22:23 +0100 Subject: [PATCH 04/86] Further fixes --- src/bitmessagemain.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 3a99fc19..87fd5934 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -517,7 +517,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s : %s', toAddress, status) + logger.warn('API Error 0007: Could not decode address: %s', toAddress + ' : ' + status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + toAddress @@ -533,7 +533,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s : %s', fromAddress, status) + logger.warn('API Error 0007: Could not decode address: %s', fromAddress + ' : ' + status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -597,7 +597,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s : %s', fromAddress, status) + logger.warn('API Error 0007: Could not decode address: %s', fromAddress + ' : ' + status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -667,7 +667,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s : %s', address, status) + logger.warn('API Error 0007: Could not decode address: %s', address + ' : ' + status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + address From ab4d53593b76be8aa1a646a17bc86ab3d38c7614 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Wed, 7 Aug 2013 21:34:46 +0100 Subject: [PATCH 05/86] Fix to python version critical log message --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 87fd5934..b3cf2df3 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -40,7 +40,7 @@ from debug import logger import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: - logger.critical("You should use python 2.7.5 or greater. Your version: %s", format(*sys.version_info)) + logger.critical("You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info)) sys.exit(0) def connectToStream(streamNumber): From 03fdbe163b2849b9c63b459b32e6423463894b8d Mon Sep 17 00:00:00 2001 From: Gregor Robinson Date: Mon, 5 Aug 2013 22:06:46 +0200 Subject: [PATCH 06/86] File permission special case for NTFS-3g on POSIX. Fix issue #347, "*SensitiveFilePermissions fails on ntfs-3g mounted filesystems". --- src/shared.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/shared.py b/src/shared.py index 214c124f..9b8c9325 100644 --- a/src/shared.py +++ b/src/shared.py @@ -346,6 +346,19 @@ def checkSensitiveFilePermissions(filename): # Windows systems. return True else: + try: + # Skip known problems for non-Win32 filesystems without POSIX permissions. + import subprocess + fstype = subprocess.check_output('stat -f -c "%%T" %s' % (filename), + shell=True, + stderr=subprocess.STDOUT) + if 'fuseblk' in fstype: + logger.info('Skipping file permissions check for %s. Filesystem fuseblk detected.', + filename) + return True + except: + # Swallow exception here, but we might run into trouble later! + logger.error('Could not determine filesystem type.', filename) present_permissions = os.stat(filename)[0] disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO return present_permissions & disallowed_permissions == 0 From f8cdfbfaa064ade856886c0758e4ead69f9da593 Mon Sep 17 00:00:00 2001 From: merlink Date: Mon, 5 Aug 2013 22:29:06 +0200 Subject: [PATCH 07/86] Changed start code for deamon mode --- src/bitmessagemain.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b156cd0d..7a2047be 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,7 +770,7 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -if __name__ == "__main__": +def main(): # is the application already running? If yes then exit. thisapp = singleton.singleinstance() @@ -840,7 +840,9 @@ if __name__ == "__main__": while True: time.sleep(20) - +if __name__ == "__main__": + main() + # So far, the creation of and management of the Bitmessage protocol and this # client is a one-man operation. Bitcoin tips are quite appreciated. # 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u From 28acbac8238c5cb365cbd8c4e967e9b730c67849 Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 10:37:31 +0200 Subject: [PATCH 08/86] Added deamon modoe to main function --- src/bitmessagemain.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 7a2047be..04ff3071 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,7 +770,7 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -def main(): +def main(deamon=False): # is the application already running? If yes then exit. thisapp = singleton.singleinstance() @@ -823,7 +823,7 @@ def main(): singleListenerThread.daemon = True # close the main program even if there are threads left singleListenerThread.start() - if not shared.safeConfigGetBoolean('bitmessagesettings', 'daemon'): + if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: try: from PyQt4 import QtCore, QtGui except Exception as err: @@ -840,6 +840,7 @@ def main(): while True: time.sleep(20) + if __name__ == "__main__": main() From 7850e9aa689baa30b6842845a1c7ea1ea8cf1cad Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 13:23:56 +0200 Subject: [PATCH 09/86] Created Object for controlling bitmessage deamon --- src/bitmessagemain.py | 139 ++++++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 59 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 04ff3071..b39bdc96 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,79 +770,100 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -def main(deamon=False): - # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() +class Main: + def start(self, deamon=False): + # is the application already running? If yes then exit. + thisapp = singleton.singleinstance() - signal.signal(signal.SIGINT, helper_generic.signal_handler) - # signal.signal(signal.SIGINT, signal.SIG_DFL) + signal.signal(signal.SIGINT, helper_generic.signal_handler) + # signal.signal(signal.SIGINT, signal.SIG_DFL) - helper_bootstrap.knownNodes() - # Start the address generation thread - addressGeneratorThread = addressGenerator() - addressGeneratorThread.daemon = True # close the main program even if there are threads left - addressGeneratorThread.start() + helper_bootstrap.knownNodes() + # Start the address generation thread + addressGeneratorThread = addressGenerator() + addressGeneratorThread.daemon = True # close the main program even if there are threads left + addressGeneratorThread.start() - # Start the thread that calculates POWs - singleWorkerThread = singleWorker() - singleWorkerThread.daemon = True # close the main program even if there are threads left - singleWorkerThread.start() + # Start the thread that calculates POWs + singleWorkerThread = singleWorker() + singleWorkerThread.daemon = True # close the main program even if there are threads left + singleWorkerThread.start() - # Start the SQL thread - sqlLookup = sqlThread() - sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. - sqlLookup.start() + # Start the SQL thread + sqlLookup = sqlThread() + sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. + sqlLookup.start() - # Start the cleanerThread - singleCleanerThread = singleCleaner() - singleCleanerThread.daemon = True # close the main program even if there are threads left - singleCleanerThread.start() + # Start the cleanerThread + singleCleanerThread = singleCleaner() + singleCleanerThread.daemon = True # close the main program even if there are threads left + singleCleanerThread.start() - shared.reloadMyAddressHashes() - shared.reloadBroadcastSendersForWhichImWatching() + shared.reloadMyAddressHashes() + shared.reloadBroadcastSendersForWhichImWatching() - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - with shared.printLock: - print 'Trying to call', apiNotifyPath + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + with shared.printLock: + print 'Trying to call', apiNotifyPath - call([apiNotifyPath, "startingUp"]) - singleAPIThread = singleAPI() - singleAPIThread.daemon = True # close the main program even if there are threads left - singleAPIThread.start() + call([apiNotifyPath, "startingUp"]) + singleAPIThread = singleAPI() + singleAPIThread.daemon = True # close the main program even if there are threads left + singleAPIThread.start() - connectToStream(1) + connectToStream(1) - singleListenerThread = singleListener() - singleListenerThread.setup(selfInitiatedConnections) - singleListenerThread.daemon = True # close the main program even if there are threads left - singleListenerThread.start() + singleListenerThread = singleListener() + singleListenerThread.setup(selfInitiatedConnections) + singleListenerThread.daemon = True # close the main program even if there are threads left + singleListenerThread.start() - if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: - try: - from PyQt4 import QtCore, QtGui - except Exception as err: - print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon' - print 'Error message:', err - os._exit(0) + if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: + try: + from PyQt4 import QtCore, QtGui + except Exception as err: + print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon' + print 'Error message:', err + os._exit(0) - import bitmessageqt - bitmessageqt.run() - else: - shared.config.remove_option('bitmessagesettings', 'dontconnect') + import bitmessageqt + bitmessageqt.run() + else: + shared.config.remove_option('bitmessagesettings', 'dontconnect') + + if deamon: + with shared.printLock: + print 'Running as a daemon. The main program should exit this thread.' + else: + with shared.printLock: + print 'Running as a daemon. You can use Ctrl+C to exit.' + while True: + time.sleep(20) + + def stop(self): with shared.printLock: - print 'Running as a daemon. You can use Ctrl+C to exit.' - - while True: - time.sleep(20) - + print 'Stopping Bitmessage Deamon.' + shared.doCleanShutdown() + + + def getApiAddress(self): + if not shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + return None + + address = shared.config.get('bitmessagesettings', 'apiinterface') + port = shared.config.getint('bitmessagesettings', 'apiport') + return {'address':address,'port':port} + if __name__ == "__main__": - main() + mainprogram = Main() + mainprogram.start() + # So far, the creation of and management of the Bitmessage protocol and this # client is a one-man operation. Bitcoin tips are quite appreciated. From 9710a861871675e4d23d0d7df5f07c2bb340cf41 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Wed, 7 Aug 2013 22:02:53 +0100 Subject: [PATCH 10/86] Minor spelling error: 'deamon' -> 'daemon' --- src/bitmessagemain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b39bdc96..629cb9ae 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -771,7 +771,7 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes / 7000) class Main: - def start(self, deamon=False): + def start(self, daemon=False): # is the application already running? If yes then exit. thisapp = singleton.singleinstance() @@ -824,7 +824,7 @@ class Main: singleListenerThread.daemon = True # close the main program even if there are threads left singleListenerThread.start() - if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: + if daemon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: try: from PyQt4 import QtCore, QtGui except Exception as err: @@ -837,7 +837,7 @@ class Main: else: shared.config.remove_option('bitmessagesettings', 'dontconnect') - if deamon: + if daemon: with shared.printLock: print 'Running as a daemon. The main program should exit this thread.' else: From a69a00d186596b8b32ad8753a5a68846d87860b3 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Fri, 9 Aug 2013 23:26:16 +0100 Subject: [PATCH 11/86] Improved logger function calls and import shared here instead of debug --- src/bitmessagemain.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index fffdafb7..44ed50b0 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -34,8 +34,7 @@ from class_addressGenerator import * # Helper Functions import helper_bootstrap -# Debug logger -from debug import logger +import shared import sys if sys.platform == 'darwin': @@ -517,7 +516,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s', toAddress + ' : ' + status) + logger.warn('API Error 0007: Could not decode address: %s:%s.', toAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + toAddress @@ -533,7 +532,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s', fromAddress + ' : ' + status) + logger.warn('API Error 0007: Could not decode address: %s:%s.', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -597,7 +596,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s', fromAddress + ' : ' + status) + logger.warn('API Error 0007: Could not decode address: %s:%s.', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -667,7 +666,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s', address + ' : ' + status) + logger.warn('API Error 0007: Could not decode address: %s:%s.', address, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + address From 326e294932763d98eb8a77f1068458f0d634803c Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Fri, 9 Aug 2013 23:32:49 +0100 Subject: [PATCH 12/86] Reverted back to importing only logger instead of all of shared --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 44ed50b0..383d82da 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -34,7 +34,7 @@ from class_addressGenerator import * # Helper Functions import helper_bootstrap -import shared +from debug import logger import sys if sys.platform == 'darwin': From a32f51f583a4f150f60d9bfa54337e1de9b7930b Mon Sep 17 00:00:00 2001 From: Erik Ackermann Date: Sat, 10 Aug 2013 13:36:02 -0400 Subject: [PATCH 13/86] Fixed OS X steps to use Homebrew commands (was using MacPorts commands) --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 1cb06c5b..151282a7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -57,8 +57,8 @@ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)" Now, install the required dependencies ``` -sudo port install python27 py27-pyqt4 openssl -sudo port install git-core +svn +doc +bash_completion +gitweb +brew install python27 py27-pyqt4 openssl +brew install git-core +svn +doc +bash_completion +gitweb ``` Download and run PyBitmessage: From 2c09326c378865fe1229a6eb5f92fb4b4be3b9dd Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sat, 10 Aug 2013 23:10:21 +0100 Subject: [PATCH 14/86] Changing text for API Error 0007 log warning --- src/bitmessagemain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 383d82da..a81767f9 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -532,7 +532,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s:%s.', fromAddress, status) + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress @@ -596,7 +596,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s:%s.', fromAddress, status) + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', fromAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + fromAddress From 53ca5b03ffb6b35ac03588107dc4a1d5cde7aad4 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sat, 10 Aug 2013 23:13:15 +0100 Subject: [PATCH 15/86] Changing text for API Error 0007 log warning (cont.) --- src/bitmessagemain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a81767f9..537b73eb 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -516,7 +516,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s:%s.', toAddress, status) + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', toAddress, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + toAddress @@ -666,7 +666,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) if status != 'success': - logger.warn('API Error 0007: Could not decode address: %s:%s.', address, status) + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) if status == 'checksumfailed': return 'API Error 0008: Checksum failed for address: ' + address From f83b636bc049208954ae99171e6ec34b17278c13 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sun, 11 Aug 2013 00:02:38 +0100 Subject: [PATCH 16/86] Converted print statement in class_sqlThread to logger calls --- src/class_sqlThread.py | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index e07850e4..5bdec02d 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -59,11 +59,10 @@ class sqlThread(threading.Thread): self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) self.conn.commit() - print 'Created messages database file' + logger.info('Created messages database file') except Exception as err: if str(err) == 'table inbox already exists': - with shared.printLock: - print 'Database file already exists.' + logger.debug('Database file already exists.') else: sys.stderr.write( @@ -145,13 +144,13 @@ class sqlThread(threading.Thread): self.cur.execute(item, parameters) if self.cur.fetchall() == []: # The settings table doesn't exist. We need to make it. - print 'In messages.dat database, creating new \'settings\' table.' + logger.debug('In messages.dat database, creating new \'settings\' table.') self.cur.execute( '''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) self.cur.execute( '''INSERT INTO settings VALUES('version','1')''') self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) - print 'In messages.dat database, removing an obsolete field from the pubkeys table.' + logger.debug('In messages.dat database, removing an obsolete field from the pubkeys table.') self.cur.execute( '''CREATE TEMPORARY TABLE pubkeys_backup(hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE);''') self.cur.execute( @@ -162,17 +161,17 @@ class sqlThread(threading.Thread): self.cur.execute( '''INSERT INTO pubkeys SELECT hash, transmitdata, time, usedpersonally FROM pubkeys_backup;''') self.cur.execute( '''DROP TABLE pubkeys_backup;''') - print 'Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times.' + logger.debug('Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times.') self.cur.execute( '''delete from inventory where objecttype = 'pubkey';''') - print 'replacing Bitmessage announcements mailing list with a new one.' + logger.debug('replacing Bitmessage announcements mailing list with a new one.') self.cur.execute( '''delete from subscriptions where address='BM-BbkPSZbzPwpVcYZpU4yHwf9ZPEapN5Zx' ''') self.cur.execute( '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') - print 'Commiting.' + logger.debug('Commiting.') self.conn.commit() - print 'Vacuuming message.dat. You might notice that the file size gets much smaller.' + logger.debug('Vacuuming message.dat. You might notice that the file size gets much smaller.') self.cur.execute( ''' VACUUM ''') # After code refactoring, the possible status values for sent messages @@ -203,11 +202,11 @@ class sqlThread(threading.Thread): self.cur.execute('''DELETE FROM pubkeys WHERE hash='1234' ''') self.conn.commit() if transmitdata == '': - sys.stderr.write('Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n') - sys.stderr.write('PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n') + logger.fatal('Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n') + logger.fatal('PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n') os._exit(0) except Exception as err: - print err + logger.error(err) # Let us check to see the last time we vaccumed the messages.dat file. # If it has been more than a month let's do it now. @@ -218,7 +217,7 @@ class sqlThread(threading.Thread): for row in queryreturn: value, = row if int(value) < int(time.time()) - 2592000: - print 'It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...' + logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...') self.cur.execute( ''' VACUUM ''') item = '''update settings set value=? WHERE key='lastvacuumtime';''' parameters = (int(time.time()),) @@ -230,13 +229,11 @@ class sqlThread(threading.Thread): self.conn.commit() elif item == 'exit': self.conn.close() - with shared.printLock: - print 'sqlThread exiting gracefully.' + logger.info('sqlThread exiting gracefully.') return elif item == 'movemessagstoprog': - with shared.printLock: - print 'the sqlThread is moving the messages.dat file to the local program directory.' + logger.debug('the sqlThread is moving the messages.dat file to the local program directory.') self.conn.commit() self.conn.close() @@ -246,8 +243,7 @@ class sqlThread(threading.Thread): self.conn.text_factory = str self.cur = self.conn.cursor() elif item == 'movemessagstoappdata': - with shared.printLock: - print 'the sqlThread is moving the messages.dat file to the Appdata folder.' + logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.') self.conn.commit() self.conn.close() @@ -268,10 +264,8 @@ class sqlThread(threading.Thread): try: self.cur.execute(item, parameters) except Exception as err: - with shared.printLock: - sys.stderr.write('\nMajor error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "' + str( - item) + '" Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: ' + str(repr(parameters)) + '\nHere is the actual error message thrown by the sqlThread: ' + str(err) + '\n') - sys.stderr.write('This program shall now abruptly exit!\n') + logger.fatal('Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s" Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s', str(item), str(repr(parameters)), str(err)) + logger.fatal('This program shall now abruptly exit!') os._exit(0) From 9d3a0a160fe152e0d4978dbd95b05e909c4c5ef5 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sun, 11 Aug 2013 00:08:48 +0100 Subject: [PATCH 17/86] Merging with master and resolving conflicts --- src/bitmessagemain.py | 114 ++++++++++++++++++++++++++++-- src/bitmessageqt/__init__.py | 5 ++ src/bitmessageqt/bitmessageui.py | 8 +-- src/bitmessageqt/bitmessageui.ui | 10 +-- src/bitmessageqt/newchandialog.py | 13 ++-- src/bitmessageqt/newchandialog.ui | 12 +++- src/bitmessageqt/settings.py | 60 ++++++++-------- src/bitmessageqt/settings.ui | 75 +++++++++++--------- src/class_singleCleaner.py | 4 +- src/class_singleWorker.py | 14 ++++ src/class_sqlThread.py | 19 ++++- src/defaultKnownNodes.py | 8 +-- src/message_data_reader.py | 15 +--- src/shared.py | 27 +++---- 14 files changed, 269 insertions(+), 115 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b39bdc96..cae8daeb 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -738,6 +738,115 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) data += ']}' return data + elif method == 'disseminatePreEncryptedMsg': + # The device issuing this command to PyBitmessage supplies a msg object that has + # already been encrypted and had the necessary proof of work done for it to be + # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this msg + # object and sends it out to the rest of the Bitmessage network as if it had generated the + # message itself. Please do not yet add this to the api doc. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + encryptedPayload, = params + encryptedPayload = encryptedPayload.decode('hex') + toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] + inventoryHash = calculateInventoryHash(encryptedPayload) + objectType = 'msg' + shared.inventory[inventoryHash] = ( + objectType, toStreamNumber, encryptedPayload, int(time.time())) + with shared.printLock: + print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + toStreamNumber, 'sendinv', inventoryHash)) + elif method == 'disseminatePubkey': + # The device issuing this command to PyBitmessage supplies a pubkey object that has + # already had the necessary proof of work done for it to be disseminated to the rest of the + # Bitmessage network. PyBitmessage accepts this pubkey object and sends it out to the + # rest of the Bitmessage network as if it had generated the pubkey object itself. Please + # do not yet add this to the api doc. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + payload, = params + payload = payload.decode('hex') + pubkeyReadPosition = 8 # bypass the nonce + if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time + pubkeyReadPosition += 8 + else: + pubkeyReadPosition += 4 + addressVersion, addressVersionLength = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10]) + pubkeyReadPosition += addressVersionLength + pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + shared.inventory[inventoryHash] = ( + objectType, pubkeyStreamNumber, payload, int(time.time())) + with shared.printLock: + print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'sendinv', inventoryHash)) + elif method == 'getMessageDataByDestinationHash': + # Method will eventually be used by a particular Android app to + # select relevant messages. Do not yet add this to the api + # doc. + + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + requestedHash, = params + if len(requestedHash) != 40: + return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + requestedHash = requestedHash.decode('hex') + + # This is not a particularly commonly used API function. Before we + # use it we'll need to fill out a field in our inventory database + # which is blank by default (first20bytesofencryptedmessage). + parameters = '' + with shared.sqlLock: + shared.sqlSubmitQueue.put('''SELECT hash, payload FROM inventory WHERE first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() + + for row in queryreturn: + hash, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+20],hash) + shared.sqlSubmitQueue.put('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + + parameters = (requestedHash,) + with shared.sqlLock: + shared.sqlSubmitQueue.put('commit') + shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() + data = '{"receivedMessageDatas":[' + for row in queryreturn: + payload, = row + if len(data) > 25: + data += ',' + data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getPubkeyByHash': + # Method will eventually be used by a particular Android app to + # retrieve pubkeys. Please do not yet add this to the api docs. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + requestedHash, = params + if len(requestedHash) != 40: + return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + requestedHash = requestedHash.decode('hex') + parameters = (requestedHash,) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() + data = '{"pubkey":[' + for row in queryreturn: + transmitdata, = row + data += json.dumps({'data':transmitdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: @@ -757,11 +866,8 @@ class singleAPI(threading.Thread): se.register_introspection_functions() se.serve_forever() +# This is a list of current connections (the thread pointers at least) selfInitiatedConnections = {} - # This is a list of current connections (the thread pointers at least) - - - if shared.useVeryEasyProofOfWorkForTesting: diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5dd4c0b5..396d0b6e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2000,6 +2000,8 @@ class MyForm(QtGui.QMainWindow): self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked())) shared.config.set('bitmessagesettings', 'startintray', str( self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked())) + shared.config.set('bitmessagesettings', 'willinglysendtomobile', str( + self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked())) if int(shared.config.get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()): if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( @@ -2996,6 +2998,8 @@ class settingsDialog(QtGui.QDialog): shared.config.getboolean('bitmessagesettings', 'showtraynotifications')) self.ui.checkBoxStartInTray.setChecked( shared.config.getboolean('bitmessagesettings', 'startintray')) + self.ui.checkBoxWillinglySendToMobile.setChecked( + shared.safeConfigGetBoolean('bitmessagesettings', 'willinglysendtomobile')) if shared.appdata == '': self.ui.checkBoxPortableMode.setChecked(True) if 'darwin' in sys.platform: @@ -3290,6 +3294,7 @@ def run(): try: translator.load("translations/bitmessage_" + str(locale.getdefaultlocale()[0])) + #translator.load("translations/bitmessage_fr_BE") # test French except: # The above is not compatible with all versions of OSX. translator.load("translations/bitmessage_en_US") # Default to english. diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index d5388182..54a3ee6f 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Thu Aug 1 00:22:41 2013 +# Created: Fri Aug 9 14:17:50 2013 # by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(775, 598) + MainWindow.resize(795, 580) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) @@ -422,7 +422,7 @@ class Ui_MainWindow(object): self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 775, 21)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 27)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -539,7 +539,7 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" +"\n" "


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 5d616be7..c2caebe0 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -6,8 +6,8 @@ 0 0 - 775 - 598 + 795 + 580 @@ -257,7 +257,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> @@ -1010,8 +1010,8 @@ p, li { white-space: pre-wrap; } 0 0 - 775 - 21 + 795 + 27 diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py index 43235bfa..65da1f1f 100644 --- a/src/bitmessageqt/newchandialog.py +++ b/src/bitmessageqt/newchandialog.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'newchandialog.ui' # -# Created: Mon Jul 22 01:05:35 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Wed Aug 7 16:51:29 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_newChanDialog(object): def setupUi(self, newChanDialog): newChanDialog.setObjectName(_fromUtf8("newChanDialog")) - newChanDialog.resize(530, 422) + newChanDialog.resize(553, 422) newChanDialog.setMinimumSize(QtCore.QSize(0, 0)) self.formLayout = QtGui.QFormLayout(newChanDialog) self.formLayout.setObjectName(_fromUtf8("formLayout")) @@ -87,13 +87,18 @@ class Ui_newChanDialog(object): QtCore.QObject.connect(self.radioButtonJoinChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxJoinChan.setShown) QtCore.QObject.connect(self.radioButtonCreateChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxCreateChan.setShown) QtCore.QMetaObject.connectSlotsByName(newChanDialog) + newChanDialog.setTabOrder(self.radioButtonJoinChan, self.radioButtonCreateChan) + newChanDialog.setTabOrder(self.radioButtonCreateChan, self.lineEditChanNameCreate) + newChanDialog.setTabOrder(self.lineEditChanNameCreate, self.lineEditChanNameJoin) + newChanDialog.setTabOrder(self.lineEditChanNameJoin, self.lineEditChanBitmessageAddress) + newChanDialog.setTabOrder(self.lineEditChanBitmessageAddress, self.buttonBox) def retranslateUi(self, newChanDialog): newChanDialog.setWindowTitle(_translate("newChanDialog", "Dialog", None)) self.radioButtonCreateChan.setText(_translate("newChanDialog", "Create a new chan", None)) self.radioButtonJoinChan.setText(_translate("newChanDialog", "Join a chan", None)) self.groupBoxCreateChan.setTitle(_translate("newChanDialog", "Create a chan", None)) - self.label_4.setText(_translate("newChanDialog", "Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private.", None)) + self.label_4.setText(_translate("newChanDialog", "

Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.

", None)) self.label_5.setText(_translate("newChanDialog", "Chan name:", None)) self.groupBoxJoinChan.setTitle(_translate("newChanDialog", "Join a chan", None)) self.label.setText(_translate("newChanDialog", "

A chan exists when a group of people share the same decryption keys. The keys and bitmessage address used by a chan are generated from a human-friendly word or phrase (the chan name). To send a message to everyone in the chan, send a normal person-to-person message to the chan address.

Chans are experimental and completely unmoderatable.

", None)) diff --git a/src/bitmessageqt/newchandialog.ui b/src/bitmessageqt/newchandialog.ui index 3713c6a3..2d42b3db 100644 --- a/src/bitmessageqt/newchandialog.ui +++ b/src/bitmessageqt/newchandialog.ui @@ -6,7 +6,7 @@ 0 0 - 530 + 553 422 @@ -46,7 +46,7 @@ - Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. + <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> true @@ -130,6 +130,14 @@
+ + radioButtonJoinChan + radioButtonCreateChan + lineEditChanNameCreate + lineEditChanNameJoin + lineEditChanBitmessageAddress + buttonBox + diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index d7672e9d..40a57f5b 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Fri Jul 12 12:37:53 2013 -# by: PyQt4 UI code generator 4.10.1 +# Created: Wed Aug 7 16:58:45 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_settingsDialog(object): def setupUi(self, settingsDialog): settingsDialog.setObjectName(_fromUtf8("settingsDialog")) - settingsDialog.resize(445, 343) + settingsDialog.resize(462, 343) self.gridLayout = QtGui.QGridLayout(settingsDialog) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) @@ -41,33 +41,36 @@ class Ui_settingsDialog(object): self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface")) self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface) self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) - self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) - self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) - self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) - self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) - self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxMinimizeToTray.setChecked(True) - self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) - self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) - self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) - self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) - self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) - self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) - self.label_7 = QtGui.QLabel(self.tabUserInterface) - self.label_7.setWordWrap(True) - self.label_7.setObjectName(_fromUtf8("label_7")) - self.gridLayout_5.addWidget(self.label_7, 5, 0, 1, 1) self.labelSettingsNote = QtGui.QLabel(self.tabUserInterface) self.labelSettingsNote.setText(_fromUtf8("")) self.labelSettingsNote.setWordWrap(True) self.labelSettingsNote.setObjectName(_fromUtf8("labelSettingsNote")) - self.gridLayout_5.addWidget(self.labelSettingsNote, 6, 0, 1, 1) + self.gridLayout_5.addWidget(self.labelSettingsNote, 7, 0, 1, 1) spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem, 7, 0, 1, 1) + self.gridLayout_5.addItem(spacerItem, 8, 0, 1, 1) + self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) + self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) + self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) + self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) + self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxMinimizeToTray.setChecked(True) + self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) + self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) + self.label_7 = QtGui.QLabel(self.tabUserInterface) + self.label_7.setWordWrap(True) + self.label_7.setObjectName(_fromUtf8("label_7")) + self.gridLayout_5.addWidget(self.label_7, 5, 0, 1, 1) + self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) + self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) + self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) + self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) + self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile")) + self.gridLayout_5.addWidget(self.checkBoxWillinglySendToMobile, 6, 0, 1, 1) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) @@ -252,12 +255,13 @@ class Ui_settingsDialog(object): def retranslateUi(self, settingsDialog): settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) - self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) - self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None)) - self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) + self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.label_7.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None)) + self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) + self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) + self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 9414e1a4..44c4f973 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -6,7 +6,7 @@ 0 0 - 445 + 462 343 @@ -37,13 +37,29 @@ User Interface - - + + - Start Bitmessage on user login + + + + true + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -51,6 +67,13 @@ + + + + Show notification when message received + + + @@ -61,20 +84,6 @@ - - - - Show notification when message received - - - - - - - Run in Portable Mode - - - @@ -85,28 +94,26 @@ - - + + - - - - true + Start Bitmessage on user login - - - - Qt::Vertical + + + + Run in Portable Mode - - - 20 - 40 - + + + + + + Willingly include unencrypted destination address when sending to a mobile device - + diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 6fed68a5..d92a37ec 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -33,9 +33,9 @@ class singleCleaner(threading.Thread): for hash, storedValue in shared.inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue if int(time.time()) - 3600 > receivedTime: - t = (hash, objectType, streamNumber, payload, receivedTime) + t = (hash, objectType, streamNumber, payload, receivedTime,'') shared.sqlSubmitQueue.put( - '''INSERT INTO inventory VALUES (?,?,?,?,?)''') + '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') shared.sqlSubmitQueue.put(t) shared.sqlReturnQueue.get() del shared.inventory[hash] diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 56f23859..b29b7f8d 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -9,6 +9,7 @@ import proofofwork import sys from class_addressGenerator import pointMult import tr +from debug import logger # This thread, of which there is only one, does the heavy lifting: # calculating POWs. @@ -517,6 +518,15 @@ class singleWorker(threading.Thread): pubkeyPayload[readPosition:readPosition + 10]) readPosition += streamNumberLength behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4] + # Mobile users may ask us to include their address's RIPE hash on a message + # unencrypted. Before we actually do it the sending human must check a box + # in the settings menu to allow it. + if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message.. + if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message.. + logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.') + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))))) + # if the human changes their setting and then sends another message or restarts their client, this one will send at that time. + continue readPosition += 4 # to bypass the bitfield of behaviors # pubSigningKeyBase256 = # pubkeyPayload[readPosition:readPosition+64] #We don't use this @@ -663,6 +673,10 @@ class singleWorker(threading.Thread): with shared.printLock: print 'Not bothering to generate ackdata because we are sending to a chan.' fullAckPayload = '' + elif not shared.isBitSetWithinBitfield(behaviorBitfield,31): + with shared.printLock: + print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.' + fullAckPayload = '' else: fullAckPayload = self.generateFullAckMessage( ackdata, toStreamNumber, embeddedTime) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 5bdec02d..97215a24 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -46,7 +46,7 @@ class sqlThread(threading.Thread): self.cur.execute( '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( - '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)''' ) + '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, first20bytesofencryptedmessage blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( '''CREATE TABLE knownnodes (timelastseen int, stream int, services blob, host blob, port blob, UNIQUE(host, stream, port) ON CONFLICT REPLACE)''' ) # This table isn't used in the program yet but I @@ -55,7 +55,7 @@ class sqlThread(threading.Thread): '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') self.cur.execute( '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) - self.cur.execute( '''INSERT INTO settings VALUES('version','1')''') + self.cur.execute( '''INSERT INTO settings VALUES('version','2')''') self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) self.conn.commit() @@ -188,7 +188,20 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'sockslisten'): shared.config.set('bitmessagesettings', 'sockslisten', 'false') - + + # Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app + item = '''SELECT value FROM settings WHERE key='version';''' + parameters = '' + self.cur.execute(item, parameters) + if int(self.cur.fetchall()[0][0]) == 1: + print 'upgrading database' + item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' ''' + parameters = '' + self.cur.execute(item, parameters) + item = '''update settings set value=? WHERE key='version';''' + parameters = (2,) + self.cur.execute(item, parameters) + try: testpayload = '\x00\x00' t = ('1234', testpayload, '12345678', 'no') diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 52e31fff..f481581c 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -37,12 +37,10 @@ def createDefaultKnownNodes(appdata): #print stream1 #print allKnownNodes - output = open(appdata + 'knownnodes.dat', 'wb') + with open(appdata + 'knownnodes.dat', 'wb') as output: + # Pickle dictionary using protocol 0. + pickle.dump(allKnownNodes, output) - # Pickle dictionary using protocol 0. - pickle.dump(allKnownNodes, output) - - output.close() return allKnownNodes def readDefaultKnownNodes(appdata): diff --git a/src/message_data_reader.py b/src/message_data_reader.py index c600935d..084ef553 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -5,19 +5,10 @@ import sqlite3 from time import strftime, localtime import sys +import shared +import string -APPNAME = "PyBitmessage" -from os import path, environ -if sys.platform == 'darwin': - if "HOME" in environ: - appdata = path.join(environ["HOME"], "Library/Application support/", APPNAME) + '/' - else: - print 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.' - sys.exit() -elif 'win' in sys.platform: - appdata = path.join(environ['APPDATA'], APPNAME) + '\\' -else: - appdata = path.expanduser(path.join("~", "." + APPNAME + "/")) +appdata = shared.lookupAppdataFolder() conn = sqlite3.connect( appdata + 'messages.dat' ) conn.text_factory = str diff --git a/src/shared.py b/src/shared.py index 9b8c9325..da83f374 100644 --- a/src/shared.py +++ b/src/shared.py @@ -101,10 +101,8 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): random.seed() payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf - userAgent = '/PyBitmessage:' + shared.softwareVersion + \ - '/' # Length of userAgent must be less than 253. - payload += pack('>B', len( - userAgent)) # user agent string length. If the user agent is more than 252 bytes long, this code isn't going to work. + userAgent = '/PyBitmessage:' + shared.softwareVersion + '/' + payload += encodeVarint(len(userAgent)) payload += userAgent payload += encodeVarint( 1) # The number of streams about which I care. PyBitmessage currently only supports 1 per connection. @@ -203,17 +201,17 @@ def decodeWalletImportFormat(WIFstring): fullString = arithmetic.changebase(WIFstring,58,256) privkey = fullString[:-4] if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]: - 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)) + logger.critical('Major problem! When trying to decode one of your private keys, the checksum ' + 'failed. Here is the PRIVATE key: %s' % str(WIFstring)) return "" else: #checksum passed if privkey[0] == '\x80': return privkey[1:] else: - logger.error('Major problem! When trying to decode one of your private keys, the ' + logger.critical('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)) + 'PRIVATE key: %s' % str(WIFstring)) return "" @@ -243,8 +241,7 @@ def reloadMyAddressHashes(): myAddressesByHash[hash] = addressInKeysFile else: - logger.error('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) @@ -320,8 +317,8 @@ def flushInventory(): sqlLock.acquire() for hash, storedValue in inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue - t = (hash,objectType,streamNumber,payload,receivedTime) - sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?)''') + t = (hash,objectType,streamNumber,payload,receivedTime,'') + sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') sqlSubmitQueue.put(t) sqlReturnQueue.get() del inventory[hash] @@ -383,6 +380,12 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys): except Exception, e: logger.exception('Keyfile permissions could not be fixed.') raise + +def isBitSetWithinBitfield(fourByteString, n): + # Uses MSB 0 bit numbering across 4 bytes of data + n = 31 - n + x, = unpack('>L', fourByteString) + return x & 2**n != 0 Peer = collections.namedtuple('Peer', ['host', 'port']) From 3ff76875aa5d8b8dbadef48e28cc7e919b9042b3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 11 Aug 2013 12:07:54 +0100 Subject: [PATCH 18/86] Packaging updated to be architecture independent --- Makefile | 3 ++- arch.sh | 2 +- archpackage/PKGBUILD | 8 ++++---- debian.sh | 4 ++-- debian/changelog | 3 ++- debian/control | 4 ++-- generate.sh | 2 +- puppypackage/pybitmessage-0.3.5.pet.specs | 2 +- rpmpackage/pybitmessage.spec | 4 +++- src/bitmessageqt/__init__.py | 10 ++++++++++ 10 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index e0ff247a..07e8b7bc 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ PREFIX?=/usr/local all: debug: source: - tar -cvzf ../${APP}_${VERSION}.orig.tar.gz ../${APP}-${VERSION} --exclude-vcs + tar -cvf ../${APP}_${VERSION}.orig.tar ../${APP}-${VERSION} --exclude-vcs + gzip -f9n ../${APP}_${VERSION}.orig.tar install: mkdir -p ${DESTDIR}/usr mkdir -p ${DESTDIR}${PREFIX} diff --git a/arch.sh b/arch.sh index 0ccc45b3..1ae5211d 100755 --- a/arch.sh +++ b/arch.sh @@ -4,7 +4,7 @@ APP=pybitmessage PREV_VERSION=0.3.5 VERSION=0.3.5 RELEASE=1 -ARCH_TYPE=`uname -m` +ARCH_TYPE=any CURRDIR=`pwd` SOURCE=archpackage/${APP}-${VERSION}.tar.gz diff --git a/archpackage/PKGBUILD b/archpackage/PKGBUILD index efbf33e2..fd13b92a 100644 --- a/archpackage/PKGBUILD +++ b/archpackage/PKGBUILD @@ -3,13 +3,13 @@ pkgname=pybitmessage pkgver=0.3.5 pkgrel=1 pkgdesc="Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide "non-content" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." -arch=('i686' 'x86_64') +arch=('any') url="https://github.com/Bitmessage/PyBitmessage" license=('MIT') groups=() -depends=('python2' 'qt4' 'python2-pyqt4' 'sqlite' 'openssl' 'gst123') +depends=('python2' 'qt4' 'python2-pyqt4' 'sqlite' 'openssl' 'mpg123') makedepends=() -optdepends=('python2-gevent') +optdepends=('python2-gevent: Python network library that uses greenlet and libevent for easy and scalable concurrency') provides=() conflicts=() replaces=() @@ -19,7 +19,7 @@ install= changelog= source=($pkgname-$pkgver.tar.gz) noextract=() -md5sums=() +md5sums=(ebf89129571571198473559b4b2e552c) build() { cd "$srcdir/$pkgname-$pkgver" ./configure --prefix=/usr diff --git a/debian.sh b/debian.sh index a4d6882e..ea46acaf 100755 --- a/debian.sh +++ b/debian.sh @@ -1,10 +1,10 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 +PREV_VERSION=0.3.5 VERSION=0.3.5 RELEASE=1 -ARCH_TYPE=`uname -m` +ARCH_TYPE=all DIR=${APP}-${VERSION} if [ $ARCH_TYPE == "x86_64" ]; then diff --git a/debian/changelog b/debian/changelog index 711f6488..8cdaff2c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,7 +14,8 @@ pybitmessage (0.3.5-1) raring; urgency=low * Added Russian translation * Added search support in the UI * Added 'make uninstall' - * To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 if PKCS5_PBKDF2_HMAC is unavailable + * To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 + if PKCS5_PBKDF2_HMAC is unavailable * Added better warnings for OSX users who are using old versions of Python * Repaired debian packaging * Altered Makefile to avoid needing to chase changes diff --git a/debian/control b/debian/control index 5e016a1c..6fb419f4 100644 --- a/debian/control +++ b/debian/control @@ -1,4 +1,5 @@ Source: pybitmessage +Section: mail Priority: extra Maintainer: Bob Mottram (4096 bits) Build-Depends: debhelper (>= 9.0.0) @@ -7,8 +8,7 @@ Homepage: https://github.com/Bitmessage/PyBitmessage Vcs-Git: https://github.com/Bitmessage/PyBitmessage.git Package: pybitmessage -Section: mail -Architecture: any +Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123 Suggests: libmessaging-menu-dev Description: Send encrypted messages diff --git a/generate.sh b/generate.sh index d21f4d83..d65f4f86 100755 --- a/generate.sh +++ b/generate.sh @@ -4,4 +4,4 @@ rm -f Makefile rpmpackage/*.spec -packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" -e "Bob Mottram (4096 bits) " --brief "Send encrypted messages" --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" --categories "Office/Email" --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" --mainscript "bitmessagemain.py" --librarypath "/opt/openssl-compat-bitcoin/lib/" --suggestsdeb "libmessaging-menu-dev" --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, python-openssl, python-sip, gst123" --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, gst123" --suggestsarch "python2-gevent" --pythonversion 2 --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" --repository "https://github.com/Bitmessage/PyBitmessage.git" +packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" -e "Bob Mottram (4096 bits) " --brief "Send encrypted messages" --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" --categories "Office/Email" --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" --mainscript "bitmessagemain.py" --librarypath "/opt/openssl-compat-bitcoin/lib/" --suggestsdeb "libmessaging-menu-dev" --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, python-openssl, python-sip, gst123" --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, mpg123" --suggestsarch "python2-gevent: Python network library that uses greenlet and libevent for easy and scalable concurrency" --pythonversion 2 --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" --repository "https://github.com/Bitmessage/PyBitmessage.git" diff --git a/puppypackage/pybitmessage-0.3.5.pet.specs b/puppypackage/pybitmessage-0.3.5.pet.specs index 939294e4..717cd281 100644 --- a/puppypackage/pybitmessage-0.3.5.pet.specs +++ b/puppypackage/pybitmessage-0.3.5.pet.specs @@ -1 +1 @@ -pybitmessage-0.3.5-1|PyBitmessage|0.3.5|1|Internet;mailnews;|7.2M||pybitmessage-0.3.5-1.pet|+openssl,+python-qt4,+sqlite3,+sqlite3-dev,+python-openssl,+python-sip,+gst123|Send encrypted messages|ubuntu|precise|5| +pybitmessage-0.3.5-1|PyBitmessage|0.3.5|1|Internet;mailnews;|3.8M||pybitmessage-0.3.5-1.pet|+openssl,+python-qt4,+sqlite3,+sqlite3-dev,+python-openssl,+python-sip,+gst123|Send encrypted messages|ubuntu|precise|5| diff --git a/rpmpackage/pybitmessage.spec b/rpmpackage/pybitmessage.spec index b15bd586..3af43532 100644 --- a/rpmpackage/pybitmessage.spec +++ b/rpmpackage/pybitmessage.spec @@ -6,6 +6,7 @@ License: MIT URL: https://github.com/Bitmessage/PyBitmessage Packager: Bob Mottram (4096 bits) Source0: http://yourdomainname.com/src/%{name}_%{version}.orig.tar.gz +BuildArch: noarch Group: Office/Email Requires: python, PyQt4, openssl-compat-bitcoin-libs, gst123 @@ -83,7 +84,8 @@ make install -B DESTDIR=%{buildroot} PREFIX=/usr - Added Russian translation - Added search support in the UI - Added 'make uninstall' -- To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 if PKCS5_PBKDF2_HMAC is unavailable +- To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 + if PKCS5_PBKDF2_HMAC is unavailable - Added better warnings for OSX users who are using old versions of Python - Repaired debian packaging - Altered Makefile to avoid needing to chase changes diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1d115af1..58452876 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1054,12 +1054,22 @@ class MyForm(QtGui.QMainWindow): if 'linux' in sys.platform: # Note: QSound was a nice idea but it didn't work if '.mp3' in soundFilename: + gst_available=False try: subprocess.call(["gst123", soundFilename], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + gst_available=True except: print "WARNING: gst123 must be installed in order to play mp3 sounds" + if not gst_available: + try: + subprocess.call(["mpg123", soundFilename], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + gst_available=True + except: + print "WARNING: mpg123 must be installed in order to play mp3 sounds" else: try: subprocess.call(["aplay", soundFilename], From 2526608c39bf60b9b12b35258abbe655844b136c Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Sun, 11 Aug 2013 15:50:47 -0400 Subject: [PATCH 19/86] Convert 'API Error' to raise APIError() Catch of unhandled exceptions and return them as new API Error 21 - Unexpected API Failure _decode method that transforms "!!!".decode('hex')errors to new API Error 22: Decode Error --- src/bitmessagemain.py | 212 +++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 94 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index cae8daeb..065410e0 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -53,6 +53,13 @@ def connectToStream(streamNumber): a.start() +class APIError(Exception): + def __init__(self, error_number, error_message): + self.error_number = error_number + self.error_message = error_message + def __str__(self): + return "API Error %04i: %s" % (self.error_number, self.error_message) + # This is one of several classes that constitute the API # This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). # http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ @@ -132,14 +139,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return False - def _dispatch(self, method, params): - self.cookies = [] - - validuser = self.APIAuthenticateClient() - if not validuser: - time.sleep(2) - return "RPC Username or password incorrect or HTTP header lacks authentication at all." - # handle request + def _decode(self, text, decode_type): + try: + return text.decode(decode_type) + except TypeError as e: + raise APIError(22, "Decode error - " + str(e)) + + def _handle_request(self, method, params): if method == 'helloWorld': (a, b) = params return a + '-' + b @@ -165,7 +171,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'createRandomAddress': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') elif len(params) == 1: label, = params eighteenByteRipe = False @@ -192,12 +198,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): payloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) else: - return 'API Error 0000: Too many parameters!' - label = label.decode('base64') + raise APIError(0, 'Too many parameters!') + label = self._decode(label, "base64") try: unicode(label, 'utf-8') except: - return 'API Error 0017: Label is not valid UTF-8 data.' + raise APIError(17, 'Label is not valid UTF-8 data.') shared.apiAddressGeneratorReturnQueue.queue.clear() streamNumberForAddress = 1 shared.addressGeneratorQueue.put(( @@ -205,7 +211,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return shared.apiAddressGeneratorReturnQueue.get() elif method == 'createDeterministicAddresses': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') elif len(params) == 1: passphrase, = params numberOfAddresses = 1 @@ -259,22 +265,22 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): payloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) else: - return 'API Error 0000: Too many parameters!' + raise APIError(0, 'Too many parameters!') if len(passphrase) == 0: - return 'API Error 0001: The specified passphrase is blank.' - passphrase = passphrase.decode('base64') + raise APIError(1, 'The specified passphrase is blank.') + passphrase = self._decode(passphrase, "base64") if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" addressVersionNumber = 3 if addressVersionNumber != 3: - return 'API Error 0002: The address version number currently must be 3 (or 0 which means auto-select). ' + addressVersionNumber + ' isn\'t supported.' + raise APIError(2,'The address version number currently must be 3 (or 0 which means auto-select). ' + addressVersionNumber + ' isn\'t supported.') if streamNumber == 0: # 0 means "just use the most available stream" streamNumber = 1 if streamNumber != 1: - return 'API Error 0003: The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.' + raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') if numberOfAddresses == 0: - return 'API Error 0004: Why would you ask me to generate 0 addresses for you?' + raise APIError(4, 'Why would you ask me to generate 0 addresses for you?') if numberOfAddresses > 999: - return 'API Error 0005: You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.' + raise APIError(5, 'You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.') shared.apiAddressGeneratorReturnQueue.queue.clear() print 'Requesting that the addressGenerator create', numberOfAddresses, 'addresses.' shared.addressGeneratorQueue.put( @@ -290,17 +296,17 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getDeterministicAddress': if len(params) != 3: - return 'API Error 0000: I need exactly 3 parameters.' + raise APIError(0, 'I need exactly 3 parameters.') passphrase, addressVersionNumber, streamNumber = params numberOfAddresses = 1 eighteenByteRipe = False if len(passphrase) == 0: - return 'API Error 0001: The specified passphrase is blank.' - passphrase = passphrase.decode('base64') + raise APIError(1, 'The specified passphrase is blank.') + passphrase = self._decode(passphrase, "base64") if addressVersionNumber != 3: - return 'API Error 0002: The address version number currently must be 3. ' + addressVersionNumber + ' isn\'t supported.' + raise APIError(2, 'The address version number currently must be 3. ' + addressVersionNumber + ' isn\'t supported.') if streamNumber != 1: - return 'API Error 0003: The stream number must be 1. Others aren\'t supported.' + raise APIError(3, ' The stream number must be 1. Others aren\'t supported.') shared.apiAddressGeneratorReturnQueue.queue.clear() print 'Requesting that the addressGenerator create', numberOfAddresses, 'addresses.' shared.addressGeneratorQueue.put( @@ -342,8 +348,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getInboxMessageById' or method == 'getInboxMessageByID': if len(params) == 0: - return 'API Error 0000: I need parameters!' - msgid = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") v = (msgid,) shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''') @@ -390,7 +396,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getInboxMessagesByReceiver' or method == 'getInboxMessagesByAddress': #after some time getInboxMessagesByAddress should be removed if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') toAddress = params[0] v = (toAddress,) shared.sqlLock.acquire() @@ -410,8 +416,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getSentMessageById' or method == 'getSentMessageByID': if len(params) == 0: - return 'API Error 0000: I need parameters!' - msgid = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") v = (msgid,) shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''') @@ -428,7 +434,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getSentMessagesByAddress' or method == 'getSentMessagesBySender': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') fromAddress = params[0] v = (fromAddress,) shared.sqlLock.acquire() @@ -448,8 +454,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'getSentMessageByAckData': if len(params) == 0: - return 'API Error 0000: I need parameters!' - ackData = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + ackData = self._decode(params[0], "hex") v = (ackData,) shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''') @@ -466,8 +472,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'trashMessage': if len(params) == 0: - return 'API Error 0000: I need parameters!' - msgid = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") # Trash if in inbox table helper_inbox.trash(msgid) @@ -483,14 +489,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return 'Trashed message (assuming message existed).' elif method == 'trashInboxMessage': if len(params) == 0: - return 'API Error 0000: I need parameters!' - msgid = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") helper_inbox.trash(msgid) return 'Trashed inbox message (assuming message existed).' elif method == 'trashSentMessage': if len(params) == 0: - return 'API Error 0000: I need parameters!' - msgid = params[0].decode('hex') + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") t = (msgid,) shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE msgid=?''') @@ -502,16 +508,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return 'Trashed sent message (assuming message existed).' elif method == 'sendMessage': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') elif len(params) == 4: toAddress, fromAddress, subject, message = params encodingType = 2 elif len(params) == 5: toAddress, fromAddress, subject, message, encodingType = params if encodingType != 2: - return 'API Error 0006: The encoding type must be 2 because that is the only one this program currently supports.' - subject = subject.decode('base64') - message = message.decode('base64') + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") status, addressVersionNumber, streamNumber, toRipe = decodeAddress( toAddress) if status != 'success': @@ -519,16 +525,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): print 'API Error 0007: Could not decode address:', toAddress, ':', status if status == 'checksumfailed': - return 'API Error 0008: Checksum failed for address: ' + toAddress + raise APIError(8, 'Checksum failed for address: ' + toAddress) if status == 'invalidcharacters': - return 'API Error 0009: Invalid characters in address: ' + toAddress + raise APIError(9, 'Invalid characters in address: ' + toAddress) if status == 'versiontoohigh': - return 'API Error 0010: Address version number too high (or zero) in address: ' + toAddress - return 'API Error 0007: Could not decode address: ' + toAddress + ' : ' + status + raise APIError(10, 'Address version number too high (or zero) in address: ' + toAddress) + raise APIError(7, 'Could not decode address: ' + toAddress + ' : ' + status) if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported. Check the toAddress.' + raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the toAddress.') if streamNumber != 1: - return 'API Error 0012: The stream number must be 1. Others aren\'t supported. Check the toAddress.' + raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the toAddress.') status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) if status != 'success': @@ -536,25 +542,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): print 'API Error 0007: Could not decode address:', fromAddress, ':', status if status == 'checksumfailed': - return 'API Error 0008: Checksum failed for address: ' + fromAddress + raise APIError(8, 'Checksum failed for address: ' + fromAddress) if status == 'invalidcharacters': - return 'API Error 0009: Invalid characters in address: ' + fromAddress + raise APIError(9, 'Invalid characters in address: ' + fromAddress) if status == 'versiontoohigh': - return 'API Error 0010: Address version number too high (or zero) in address: ' + fromAddress - return 'API Error 0007: Could not decode address: ' + fromAddress + ' : ' + status + raise APIError(10, 'Address version number too high (or zero) in address: ' + fromAddress) + raise APIError(7, 'Could not decode address: ' + fromAddress + ' : ' + status) if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.' + raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.') if streamNumber != 1: - return 'API Error 0012: The stream number must be 1. Others aren\'t supported. Check the fromAddress.' + raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the fromAddress.') toAddress = addBMIfNotPresent(toAddress) fromAddress = addBMIfNotPresent(fromAddress) try: fromAddressEnabled = shared.config.getboolean( fromAddress, 'enabled') except: - return 'API Error 0013: Could not find your fromAddress in the keys.dat file.' + raise APIError(13, 'Could not find your fromAddress in the keys.dat file.') if not fromAddressEnabled: - return 'API Error 0014: Your fromAddress is disabled. Cannot send.' + raise APIError(14, 'Your fromAddress is disabled. Cannot send.') ackdata = OpenSSL.rand(32) @@ -583,16 +589,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'sendBroadcast': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') if len(params) == 3: fromAddress, subject, message = params encodingType = 2 elif len(params) == 4: fromAddress, subject, message, encodingType = params if encodingType != 2: - return 'API Error 0006: The encoding type must be 2 because that is the only one this program currently supports.' - subject = subject.decode('base64') - message = message.decode('base64') + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( fromAddress) @@ -601,22 +607,22 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): print 'API Error 0007: Could not decode address:', fromAddress, ':', status if status == 'checksumfailed': - return 'API Error 0008: Checksum failed for address: ' + fromAddress + raise APIError(8, 'Checksum failed for address: ' + fromAddress) if status == 'invalidcharacters': - return 'API Error 0009: Invalid characters in address: ' + fromAddress + raise APIError(9, 'Invalid characters in address: ' + fromAddress) if status == 'versiontoohigh': - return 'API Error 0010: Address version number too high (or zero) in address: ' + fromAddress - return 'API Error 0007: Could not decode address: ' + fromAddress + ' : ' + status + raise APIError(10, 'Address version number too high (or zero) in address: ' + fromAddress) + raise APIError(7, 'Could not decode address: ' + fromAddress + ' : ' + status) if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: the address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.' + raise APIError(11, 'the address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.') if streamNumber != 1: - return 'API Error 0012: the stream number must be 1. Others aren\'t supported. Check the fromAddress.' + raise APIError(12, 'the stream number must be 1. Others aren\'t supported. Check the fromAddress.') fromAddress = addBMIfNotPresent(fromAddress) try: fromAddressEnabled = shared.config.getboolean( fromAddress, 'enabled') except: - return 'API Error 0013: could not find your fromAddress in the keys.dat file.' + raise APIError(13, 'could not find your fromAddress in the keys.dat file.') ackdata = OpenSSL.rand(32) toAddress = '[Broadcast subscribers]' ripe = '' @@ -634,14 +640,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return ackdata.encode('hex') elif method == 'getStatus': if len(params) != 1: - return 'API Error 0000: I need one parameter!' + raise APIError(0, 'I need one parameter!') ackdata, = params if len(ackdata) != 64: - return 'API Error 0015: The length of ackData should be 32 bytes (encoded in hex thus 64 characters).' + raise APIError(15, 'The length of ackData should be 32 bytes (encoded in hex thus 64 characters).') + ackdata = self._decode(ackdata, "hex") shared.sqlLock.acquire() shared.sqlSubmitQueue.put( '''SELECT status FROM sent where ackdata=?''') - shared.sqlSubmitQueue.put((ackdata.decode('hex'),)) + shared.sqlSubmitQueue.put((ackdata,)) queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() if queryreturn == []: @@ -651,19 +658,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return status elif method == 'addSubscription': if len(params) == 0: - return 'API Error 0000: I need parameters!' + raise APIError(0, 'I need parameters!') if len(params) == 1: address, = params label == '' if len(params) == 2: address, label = params - label = label.decode('base64') + label = self._decode(label, "base64") try: unicode(label, 'utf-8') except: - return 'API Error 0017: Label is not valid UTF-8 data.' + raise APIError(17, 'Label is not valid UTF-8 data.') if len(params) > 2: - return 'API Error 0000: I need either 1 or 2 parameters!' + raise APIError(0, 'I need either 1 or 2 parameters!') address = addBMIfNotPresent(address) status, addressVersionNumber, streamNumber, toRipe = decodeAddress( address) @@ -672,16 +679,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): print 'API Error 0007: Could not decode address:', address, ':', status if status == 'checksumfailed': - return 'API Error 0008: Checksum failed for address: ' + address + raise APIError(8, 'Checksum failed for address: ' + address) if status == 'invalidcharacters': - return 'API Error 0009: Invalid characters in address: ' + address + raise APIError(9, 'Invalid characters in address: ' + address) if status == 'versiontoohigh': - return 'API Error 0010: Address version number too high (or zero) in address: ' + address - return 'API Error 0007: Could not decode address: ' + address + ' : ' + status + raise APIError(10, 'Address version number too high (or zero) in address: ' + address) + raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported.' + raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported.') if streamNumber != 1: - return 'API Error 0012: The stream number must be 1. Others aren\'t supported.' + raise APIError(12, 'The stream number must be 1. Others aren\'t supported.') # First we must check to see if the address is already in the # subscriptions list. shared.sqlLock.acquire() @@ -692,7 +699,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() if queryreturn != []: - return 'API Error 0016: You are already subscribed to that address.' + raise APIError(16, 'You are already subscribed to that address.') t = (label, address, True) shared.sqlLock.acquire() shared.sqlSubmitQueue.put( @@ -708,7 +715,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'deleteSubscription': if len(params) != 1: - return 'API Error 0000: I need 1 parameter!' + raise APIError(0, 'I need 1 parameter!') address, = params address = addBMIfNotPresent(address) t = (address,) @@ -745,9 +752,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # object and sends it out to the rest of the Bitmessage network as if it had generated the # message itself. Please do not yet add this to the api doc. if len(params) != 1: - return 'API Error 0000: I need 1 parameter!' + raise APIError(0, 'I need 1 parameter!') encryptedPayload, = params - encryptedPayload = encryptedPayload.decode('hex') + encryptedPayload = self._decode(encryptedPayload, "hex") toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' @@ -764,9 +771,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # rest of the Bitmessage network as if it had generated the pubkey object itself. Please # do not yet add this to the api doc. if len(params) != 1: - return 'API Error 0000: I need 1 parameter!' + raise APIError(0, 'I need 1 parameter!') payload, = params - payload = payload.decode('hex') + payload = self._decode(payload, "hex") pubkeyReadPosition = 8 # bypass the nonce if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time pubkeyReadPosition += 8 @@ -789,11 +796,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # doc. if len(params) != 1: - return 'API Error 0000: I need 1 parameter!' + raise APIError(0, 'I need 1 parameter!') requestedHash, = params if len(requestedHash) != 40: - return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' - requestedHash = requestedHash.decode('hex') + raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).') + requestedHash = self._decode(requestedHash, "hex") # This is not a particularly commonly used API function. Before we # use it we'll need to fill out a field in our inventory database @@ -831,11 +838,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # Method will eventually be used by a particular Android app to # retrieve pubkeys. Please do not yet add this to the api docs. if len(params) != 1: - return 'API Error 0000: I need 1 parameter!' + raise APIError(0, 'I need 1 parameter!') requestedHash, = params if len(requestedHash) != 40: - return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' - requestedHash = requestedHash.decode('hex') + raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).') + requestedHash = self._decode(requestedHash, "hex") parameters = (requestedHash,) with shared.sqlLock: shared.sqlSubmitQueue.put('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''') @@ -850,7 +857,24 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: - return 'API Error 0020: Invalid method: %s' % method + raise APIError(20, 'Invalid method: %s' % method) + + def _dispatch(self, method, params): + self.cookies = [] + + validuser = self.APIAuthenticateClient() + if not validuser: + time.sleep(2) + return "RPC Username or password incorrect or HTTP header lacks authentication at all." + + try: + return self._handle_request(method, params) + except APIError as e: + return str(e) + except Exception as e: + print(e) + print(sys.exc_info()[0]) + return "API Error 0021: Unexpected API Failure - %s" % str(e) # This thread, of which there is only one, runs the API. From f6a07a374a5c94bcaba350a92953d6dff1b41478 Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Mon, 12 Aug 2013 18:13:28 -0500 Subject: [PATCH 20/86] Add backend ability to understand shorter addresses. Introduces addresses version 4. --- src/addresses.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index a6a571f6..46fa35ba 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -97,13 +97,23 @@ def calculateInventoryHash(data): return sha2.digest()[0:32] def encodeAddress(version,stream,ripe): - if version >= 2: + if version >= 2 and version < 4: if len(ripe) != 20: raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.") if ripe[:2] == '\x00\x00': ripe = ripe[2:] elif ripe[:1] == '\x00': ripe = ripe[1:] + elif version == 4: + if len(ripe) != 20: + raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.") + emptybitcounter = 0 + while True: + if ripe[emptybitcounter] != '\x00': + break + emptybitcounter += 1 + ripe = ripe[emptybitcounter:] + a = encodeVarint(version) + encodeVarint(stream) + ripe sha = hashlib.new('sha512') sha.update(a) @@ -164,7 +174,7 @@ def decodeAddress(address): #print 'addressVersionNumber', addressVersionNumber #print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber - if addressVersionNumber > 3: + if addressVersionNumber > 4: print 'cannot decode address version numbers this high' status = 'versiontoohigh' return status,0,0,0 @@ -191,6 +201,17 @@ def decodeAddress(address): return 'ripetoolong',0,0,0 else: return 'otherproblem',0,0,0 + elif addressVersionNumber == 4: + if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) > 20: + return 'ripetoolong',0,0,0 + elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 4: + return 'ripetooshort',0,0,0 + else: + x00string = '' + for i in range(20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4])): + x00string += '\x00' + return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] + def addBMIfNotPresent(address): address = str(address).strip() From f3e8ce3b8206b69d630ee7566d2d30f50ebceeb1 Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Mon, 12 Aug 2013 20:59:38 -0500 Subject: [PATCH 21/86] Made changes suggested by nimdahk --- src/addresses.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index 46fa35ba..1db2105d 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -107,13 +107,8 @@ def encodeAddress(version,stream,ripe): elif version == 4: if len(ripe) != 20: raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.") - emptybitcounter = 0 - while True: - if ripe[emptybitcounter] != '\x00': - break - emptybitcounter += 1 - ripe = ripe[emptybitcounter:] - + ripe = ripe.lstrip('\x00') + a = encodeVarint(version) + encodeVarint(stream) + ripe sha = hashlib.new('sha512') sha.update(a) @@ -207,9 +202,7 @@ def decodeAddress(address): elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 4: return 'ripetooshort',0,0,0 else: - x00string = '' - for i in range(20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4])): - x00string += '\x00' + x00string = '\x00' * (20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4])) return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] From 782214c7b799cd24718136eb931e081bf5e08c36 Mon Sep 17 00:00:00 2001 From: UnderSampled Date: Wed, 14 Aug 2013 23:21:05 -0400 Subject: [PATCH 22/86] Allow inbox and sent preview panels to resize. --- src/bitmessageqt/bitmessageui.py | 30 ++-- src/bitmessageqt/bitmessageui.ui | 269 ++++++++++++++++--------------- 2 files changed, 159 insertions(+), 140 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index c051c076..0608c733 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Mon Aug 12 00:08:20 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Wed Aug 14 23:19:43 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -69,7 +69,10 @@ class Ui_MainWindow(object): self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.horizontalLayoutSearch.addWidget(self.inboxSearchOptionCB) self.verticalLayout_2.addLayout(self.horizontalLayoutSearch) - self.tableWidgetInbox = QtGui.QTableWidget(self.inbox) + self.splitter = QtGui.QSplitter(self.inbox) + self.splitter.setOrientation(QtCore.Qt.Vertical) + self.splitter.setObjectName(_fromUtf8("splitter")) + self.tableWidgetInbox = QtGui.QTableWidget(self.splitter) self.tableWidgetInbox.setAlternatingRowColors(True) self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.tableWidgetInbox.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) @@ -93,11 +96,10 @@ class Ui_MainWindow(object): self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True) self.tableWidgetInbox.verticalHeader().setVisible(False) self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_2.addWidget(self.tableWidgetInbox) - self.textEditInboxMessage = QtGui.QTextEdit(self.inbox) + self.textEditInboxMessage = QtGui.QTextEdit(self.splitter) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) - self.verticalLayout_2.addWidget(self.textEditInboxMessage) + self.verticalLayout_2.addWidget(self.splitter) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.inbox, icon1, _fromUtf8("")) @@ -193,7 +195,10 @@ class Ui_MainWindow(object): self.sentSearchOptionCB.addItem(_fromUtf8("")) self.horizontalLayout.addWidget(self.sentSearchOptionCB) self.verticalLayout.addLayout(self.horizontalLayout) - self.tableWidgetSent = QtGui.QTableWidget(self.sent) + self.splitter_2 = QtGui.QSplitter(self.sent) + self.splitter_2.setOrientation(QtCore.Qt.Vertical) + self.splitter_2.setObjectName(_fromUtf8("splitter_2")) + self.tableWidgetSent = QtGui.QTableWidget(self.splitter_2) self.tableWidgetSent.setDragDropMode(QtGui.QAbstractItemView.DragDrop) self.tableWidgetSent.setAlternatingRowColors(True) self.tableWidgetSent.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) @@ -217,10 +222,9 @@ class Ui_MainWindow(object): self.tableWidgetSent.horizontalHeader().setStretchLastSection(True) self.tableWidgetSent.verticalHeader().setVisible(False) self.tableWidgetSent.verticalHeader().setStretchLastSection(False) - self.verticalLayout.addWidget(self.tableWidgetSent) - self.textEditSentMessage = QtGui.QTextEdit(self.sent) + self.textEditSentMessage = QtGui.QTextEdit(self.splitter_2) self.textEditSentMessage.setObjectName(_fromUtf8("textEditSentMessage")) - self.verticalLayout.addWidget(self.textEditSentMessage) + self.verticalLayout.addWidget(self.splitter_2) icon3 = QtGui.QIcon() icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/sent.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.sent, icon3, _fromUtf8("")) @@ -428,7 +432,7 @@ class Ui_MainWindow(object): self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 18)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 23)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -546,8 +550,8 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" -"


", None)) +"\n" +"


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 683703d8..226e6a64 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -22,7 +22,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -112,76 +121,79 @@ - - - true + + + Qt::Vertical - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - 200 - - - false - - - 27 - - - false - - - true - - - false - - - 26 - - - - To + + + true - - - - From + + QAbstractItemView::ExtendedSelection - - - - Subject + + QAbstractItemView::SelectRows - - - - Received + + true - - - - - - - - 0 - 500 - - + + false + + + true + + + 200 + + + false + + + 27 + + + false + + + true + + + false + + + 26 + + + + To + + + + + From + + + + + Subject + + + + + Received + + + + + + + 0 + 500 + + + @@ -269,8 +281,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> @@ -409,71 +421,74 @@ p, li { white-space: pre-wrap; } - - - QAbstractItemView::DragDrop + + + Qt::Vertical - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - 130 - - - false - - - false - - - true - - - false - - - false - - - - To + + + QAbstractItemView::DragDrop - - - - From + + true - - - - Subject + + QAbstractItemView::ExtendedSelection - - - - Status + + QAbstractItemView::SelectRows - + + true + + + false + + + true + + + 130 + + + false + + + false + + + true + + + false + + + false + + + + To + + + + + From + + + + + Subject + + + + + Status + + + + - - - @@ -1023,7 +1038,7 @@ p, li { white-space: pre-wrap; } 0 0 795 - 18 + 23 From 2a565c97a50792af9874e0276f94efccda1a7c1d Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Thu, 15 Aug 2013 03:51:46 -0500 Subject: [PATCH 23/86] Allow backend to send and receive version 4 addresses --- src/class_singleWorker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index b29b7f8d..1f48701c 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -540,7 +540,7 @@ class singleWorker(threading.Thread): requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this.")))) - elif toAddressVersionNumber == 3: + elif toAddressVersionNumber == 3 or toAddressVersionNumber == 4: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( pubkeyPayload[readPosition:readPosition + 10]) readPosition += varintLength @@ -617,7 +617,7 @@ class singleWorker(threading.Thread): payload += encodeVarint(len(signature)) payload += signature - if fromAddressVersionNumber == 3: + if fromAddressVersionNumber == 3 or fromAddressVersionNumber == 4: payload = '\x01' # Message version. payload += encodeVarint(fromAddressVersionNumber) payload += encodeVarint(fromStreamNumber) From ef312c6e2c519f7534bb81743e2fa6e07ce95c79 Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Thu, 15 Aug 2013 04:26:14 -0500 Subject: [PATCH 24/86] Updated several missed references to version 3 addresses --- src/bitmessagemain.py | 16 ++++++++-------- src/bitmessageqt/__init__.py | 2 +- src/class_receiveDataThread.py | 4 ++-- src/shared.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index cae8daeb..7ec8ee0e 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -525,8 +525,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if status == 'versiontoohigh': return 'API Error 0010: Address version number too high (or zero) in address: ' + toAddress return 'API Error 0007: Could not decode address: ' + toAddress + ' : ' + status - if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported. Check the toAddress.' + if addressVersionNumber < 2 or addressVersionNumber > 4: + return 'API Error 0011: The address version number currently must be 2, 3, or 4. Others aren\'t supported. Check the toAddress.' if streamNumber != 1: return 'API Error 0012: The stream number must be 1. Others aren\'t supported. Check the toAddress.' status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( @@ -542,8 +542,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if status == 'versiontoohigh': return 'API Error 0010: Address version number too high (or zero) in address: ' + fromAddress return 'API Error 0007: Could not decode address: ' + fromAddress + ' : ' + status - if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.' + if addressVersionNumber < 2 or addressVersionNumber > 4: + return 'API Error 0011: The address version number currently must be 2, 3, or 4. Others aren\'t supported. Check the fromAddress.' if streamNumber != 1: return 'API Error 0012: The stream number must be 1. Others aren\'t supported. Check the fromAddress.' toAddress = addBMIfNotPresent(toAddress) @@ -607,8 +607,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if status == 'versiontoohigh': return 'API Error 0010: Address version number too high (or zero) in address: ' + fromAddress return 'API Error 0007: Could not decode address: ' + fromAddress + ' : ' + status - if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: the address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.' + if addressVersionNumber < 2 or addressVersionNumber > 4: + return 'API Error 0011: the address version number currently must be 2, 3, or 4. Others aren\'t supported. Check the fromAddress.' if streamNumber != 1: return 'API Error 0012: the stream number must be 1. Others aren\'t supported. Check the fromAddress.' fromAddress = addBMIfNotPresent(fromAddress) @@ -678,8 +678,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if status == 'versiontoohigh': return 'API Error 0010: Address version number too high (or zero) in address: ' + address return 'API Error 0007: Could not decode address: ' + address + ' : ' + status - if addressVersionNumber < 2 or addressVersionNumber > 3: - return 'API Error 0011: The address version number currently must be 2 or 3. Others aren\'t supported.' + if addressVersionNumber < 2 or addressVersionNumber > 4: + return 'API Error 0011: The address version number currently must be 2, 3, or 4. Others aren\'t supported.' if streamNumber != 1: return 'API Error 0012: The stream number must be 1. Others aren\'t supported.' # First we must check to see if the address is already in the diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 396d0b6e..c50152c0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1572,7 +1572,7 @@ class MyForm(QtGui.QMainWindow): continue except: pass - if addressVersionNumber > 3 or addressVersionNumber <= 1: + if addressVersionNumber > 4 or addressVersionNumber <= 1: QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( "MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber))) continue diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 6adfe490..fb37792b 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -1216,7 +1216,7 @@ class receiveDataThread(threading.Thread): if addressVersion == 0: print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.' return - if addressVersion >= 4 or addressVersion == 1: + if addressVersion > 4 or addressVersion == 1: with shared.printLock: print 'This version of Bitmessage cannot handle version', addressVersion, 'addresses.' @@ -1273,7 +1273,7 @@ class receiveDataThread(threading.Thread): shared.sqlLock.release() # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) self.possibleNewPubkey(ripe) - if addressVersion == 3: + if addressVersion == 3 or addressVersion == 4: if len(data) < 170: # sanity check. print '(within processpubkey) payloadLength less than 170. Sanity check failed.' return diff --git a/src/shared.py b/src/shared.py index da83f374..e5ff8378 100644 --- a/src/shared.py +++ b/src/shared.py @@ -230,7 +230,7 @@ def reloadMyAddressHashes(): if isEnabled: hasEnabledKeys = True status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) - if addressVersionNumber == 2 or addressVersionNumber == 3: + if addressVersionNumber == 2 or addressVersionNumber == 3 or addressVersionNumber == 4: # Returns a simple 32 bytes of information encoded in 64 Hex characters, # or null if there was an error. privEncryptionKey = decodeWalletImportFormat( From 13f029f34c7414b5a9b80670fa5d7a50dd372710 Mon Sep 17 00:00:00 2001 From: UnderSampled Date: Thu, 15 Aug 2013 10:01:36 -0400 Subject: [PATCH 25/86] Set inbox and sent preview panels to read only. --- src/bitmessageqt/bitmessageui.py | 4 +++- src/bitmessageqt/bitmessageui.ui | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 0608c733..942f6bf1 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Wed Aug 14 23:19:43 2013 +# Created: Thu Aug 15 09:54:36 2013 # by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -98,6 +98,7 @@ class Ui_MainWindow(object): self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) self.textEditInboxMessage = QtGui.QTextEdit(self.splitter) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) + self.textEditInboxMessage.setReadOnly(True) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) self.verticalLayout_2.addWidget(self.splitter) icon1 = QtGui.QIcon() @@ -223,6 +224,7 @@ class Ui_MainWindow(object): self.tableWidgetSent.verticalHeader().setVisible(False) self.tableWidgetSent.verticalHeader().setStretchLastSection(False) self.textEditSentMessage = QtGui.QTextEdit(self.splitter_2) + self.textEditSentMessage.setReadOnly(True) self.textEditSentMessage.setObjectName(_fromUtf8("textEditSentMessage")) self.verticalLayout.addWidget(self.splitter_2) icon3 = QtGui.QIcon() diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 226e6a64..22b6c314 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -193,6 +193,9 @@ 500 + + true + @@ -486,7 +489,11 @@ p, li { white-space: pre-wrap; } - + + + true + + From 85fc2682f086d5f9beb4e115fbcd0b363d215646 Mon Sep 17 00:00:00 2001 From: UnderSampled Date: Thu, 15 Aug 2013 14:21:07 -0400 Subject: [PATCH 26/86] remove inbox and sent tables edit triggers. --- src/bitmessageqt/bitmessageui.py | 4 +++- src/bitmessageqt/bitmessageui.ui | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 942f6bf1..74505f38 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Thu Aug 15 09:54:36 2013 +# Created: Thu Aug 15 14:19:52 2013 # by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -73,6 +73,7 @@ class Ui_MainWindow(object): self.splitter.setOrientation(QtCore.Qt.Vertical) self.splitter.setObjectName(_fromUtf8("splitter")) self.tableWidgetInbox = QtGui.QTableWidget(self.splitter) + self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setAlternatingRowColors(True) self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.tableWidgetInbox.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) @@ -200,6 +201,7 @@ class Ui_MainWindow(object): self.splitter_2.setOrientation(QtCore.Qt.Vertical) self.splitter_2.setObjectName(_fromUtf8("splitter_2")) self.tableWidgetSent = QtGui.QTableWidget(self.splitter_2) + self.tableWidgetSent.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetSent.setDragDropMode(QtGui.QAbstractItemView.DragDrop) self.tableWidgetSent.setAlternatingRowColors(True) self.tableWidgetSent.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 22b6c314..5b597d38 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -126,6 +126,9 @@ Qt::Vertical + + QAbstractItemView::NoEditTriggers + true @@ -429,6 +432,9 @@ p, li { white-space: pre-wrap; } Qt::Vertical + + QAbstractItemView::NoEditTriggers + QAbstractItemView::DragDrop From 738c58694c78497bc80fbef5192ceefe321db31b Mon Sep 17 00:00:00 2001 From: Erik Ackermann Date: Fri, 16 Aug 2013 19:02:40 -0400 Subject: [PATCH 27/86] Responded to comment from jvz; fixed brew install instructions. --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 151282a7..4eb896eb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -57,8 +57,8 @@ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)" Now, install the required dependencies ``` -brew install python27 py27-pyqt4 openssl -brew install git-core +svn +doc +bash_completion +gitweb +brew install python pyqt +brew install git ``` Download and run PyBitmessage: From 16ff6e883a0f7f1f8d78c69e8d88fc96e3b87fcc Mon Sep 17 00:00:00 2001 From: Tim van Werkhoven Date: Tue, 20 Aug 2013 10:43:30 +0200 Subject: [PATCH 28/86] Use 'inf' as large value instead of 1e20 'inf' is always bigger than any number, 1e20 is not. --- src/proofofwork.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index f2c32c06..6e2b7bc3 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -24,7 +24,7 @@ def _set_idle(): def _pool_worker(nonce, initialHash, target, pool_size): _set_idle() - trialValue = 99999999999999999999 + trialValue = float('inf') while trialValue > target: nonce += pool_size trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) @@ -32,7 +32,7 @@ def _pool_worker(nonce, initialHash, target, pool_size): def _doSafePoW(target, initialHash): nonce = 0 - trialValue = 99999999999999999999 + trialValue = float('inf') while trialValue > target: nonce += 1 trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) From bcff27ff7c5d6166e935c26ae57e8ed5384d5f62 Mon Sep 17 00:00:00 2001 From: Matthieu Rakotojaona Date: Wed, 21 Aug 2013 00:02:57 +0200 Subject: [PATCH 29/86] Fix Archlinux package creation --- arch.sh | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/arch.sh b/arch.sh index 0ccc45b3..6ca4aec9 100755 --- a/arch.sh +++ b/arch.sh @@ -1,5 +1,6 @@ #!/bin/bash +GIT_APP=PyBitmessage APP=pybitmessage PREV_VERSION=0.3.5 VERSION=0.3.5 @@ -25,24 +26,12 @@ make clean rm -f archpackage/*.gz # having the root directory called name-version seems essential -mv ../${APP} ../${APP}-${VERSION} +mv ../${GIT_APP} ../${APP}-${VERSION} tar -cvzf ${SOURCE} ../${APP}-${VERSION} --exclude-vcs # rename the root directory without the version number -mv ../${APP}-${VERSION} ../${APP} +mv ../${APP}-${VERSION} ../${GIT_APP} # calculate the MD5 checksum CHECKSM=$(md5sum ${SOURCE}) sed -i "s/md5sums[^)]*)/md5sums=(${CHECKSM%% *})/g" archpackage/PKGBUILD - -cd archpackage - -# Create the package -tar -c -f ${APP}-${VERSION}.pkg.tar . -sync -xz ${APP}-${VERSION}.pkg.tar -sync - -# Move back to the original directory -cd ${CURRDIR} - From a290b61f1a3a6223b5405d68fd10aa1957887c1c Mon Sep 17 00:00:00 2001 From: Amos Bairn Date: Thu, 22 Aug 2013 07:35:48 -0700 Subject: [PATCH 30/86] Add listAddressbook to api listAddressbook returns label and address for each address in the addressbook. --- src/bitmessagemain.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index fd233bd5..00c79247 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -164,6 +164,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled')}, indent=4, separators=(',', ': ')) data += ']}' return data + elif method == 'listAddressbook': + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''SELECT label, address from addressbook''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + data = '{"addresses":[' + for row in queryreturn: + label, address = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address}) + data += ']}' + return data elif method == 'createRandomAddress': if len(params) == 0: return 'API Error 0000: I need parameters!' From a20213f1e834b88646f43ba54b31404f1869bf52 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Fri, 23 Aug 2013 13:20:07 -0400 Subject: [PATCH 31/86] Use fast PoW unless we're explicitly a frozen (binary) version of the code --- src/proofofwork.py | 4 ++-- src/shared.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index f2c32c06..2238eaec 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -4,7 +4,7 @@ import hashlib from struct import unpack, pack import sys -from shared import config +from shared import config, frozen #import os def _set_idle(): @@ -71,7 +71,7 @@ def _doFastPoW(target, initialHash): time.sleep(0.2) def run(target, initialHash): - if 'linux' in sys.platform: + if not frozen: return _doFastPoW(target, initialHash) else: return _doSafePoW(target, initialHash) diff --git a/src/shared.py b/src/shared.py index d2b82d11..13d57445 100644 --- a/src/shared.py +++ b/src/shared.py @@ -74,6 +74,11 @@ networkDefaultPayloadLengthExtraBytes = 14000 #To make sending short messages a # namecoin integration to "namecoind". namecoinDefaultRpcPort = "8336" +# When using py2exe or py2app, the variable frozen is added to the sys +# namespace. This can be used to setup a different code path for +# binary distributions vs source distributions. +frozen = getattr(sys,'frozen', None) + def isInSqlInventory(hash): t = (hash,) shared.sqlLock.acquire() From bd489408c71e68430d7d73fcced20c9edd75ca90 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Fri, 23 Aug 2013 16:10:57 -0400 Subject: [PATCH 32/86] Actually OSX app maded with py2app can parallelize just fine --- src/proofofwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index 2238eaec..946d125a 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -71,7 +71,7 @@ def _doFastPoW(target, initialHash): time.sleep(0.2) def run(target, initialHash): - if not frozen: + if frozen == "macosx_app" or not frozen: return _doFastPoW(target, initialHash) else: return _doSafePoW(target, initialHash) From 0132db33dca24840fce680050fbbe77910715b38 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 24 Aug 2013 19:40:48 -0400 Subject: [PATCH 33/86] show number of each message type processed in the API command clientStatus --- src/bitmessagemain.py | 8 +++++++- src/bitmessageqt/__init__.py | 33 +++++++++++++++------------------ src/class_receiveDataThread.py | 10 +++++++--- src/shared.py | 4 ++++ 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index fd233bd5..8443a59f 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -849,7 +849,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data elif method == 'clientStatus': - return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) + if len(shared.connectedHostsList) == 0: + networkStatus = 'notConnected' + elif len(shared.connectedHostsList) > 0 and not shared.clientHasReceivedIncomingConnections: + networkStatus = 'connectedButHaveNotReceivedIncomingConnections' + else: + networkStatus = 'connectedAndReceivingIncomingConnections' + return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus}, indent=4, separators=(',', ': ')) else: return 'API Error 0020: Invalid method: %s' % method diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 378989e0..28942f10 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -417,11 +417,11 @@ class MyForm(QtGui.QMainWindow): QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "updateNetworkStatusTab()"), self.updateNetworkStatusTab) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( - "incrementNumberOfMessagesProcessed()"), self.incrementNumberOfMessagesProcessed) + "updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( - "incrementNumberOfPubkeysProcessed()"), self.incrementNumberOfPubkeysProcessed) + "updateNumberOfPubkeysProcessed()"), self.updateNumberOfPubkeysProcessed) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( - "incrementNumberOfBroadcastsProcessed()"), self.incrementNumberOfBroadcastsProcessed) + "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "setStatusIcon(PyQt_PyObject)"), self.setStatusIcon) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( @@ -1260,20 +1260,17 @@ class MyForm(QtGui.QMainWindow): self.actionShow.setChecked(not self.actionShow.isChecked()) self.appIndicatorShowOrHideWindow() - def incrementNumberOfMessagesProcessed(self): - self.numberOfMessagesProcessed += 1 + def updateNumberOfMessagesProcessed(self): self.ui.labelMessageCount.setText(_translate( - "MainWindow", "Processed %1 person-to-person messages.").arg(str(self.numberOfMessagesProcessed))) + "MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed))) - def incrementNumberOfBroadcastsProcessed(self): - self.numberOfBroadcastsProcessed += 1 + def updateNumberOfBroadcastsProcessed(self): self.ui.labelBroadcastCount.setText(_translate( - "MainWindow", "Processed %1 broadcast messages.").arg(str(self.numberOfBroadcastsProcessed))) + "MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed))) - def incrementNumberOfPubkeysProcessed(self): - self.numberOfPubkeysProcessed += 1 + def updateNumberOfPubkeysProcessed(self): self.ui.labelPubkeyCount.setText(_translate( - "MainWindow", "Processed %1 public keys.").arg(str(self.numberOfPubkeysProcessed))) + "MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed))) def updateNetworkStatusTab(self): # print 'updating network status tab' @@ -3363,12 +3360,12 @@ class UISignaler(Thread,QThread): toAddress, fromLabel, fromAddress, subject, message, ackdata) elif command == 'updateNetworkStatusTab': self.emit(SIGNAL("updateNetworkStatusTab()")) - elif command == 'incrementNumberOfMessagesProcessed': - self.emit(SIGNAL("incrementNumberOfMessagesProcessed()")) - elif command == 'incrementNumberOfPubkeysProcessed': - self.emit(SIGNAL("incrementNumberOfPubkeysProcessed()")) - elif command == 'incrementNumberOfBroadcastsProcessed': - self.emit(SIGNAL("incrementNumberOfBroadcastsProcessed()")) + elif command == 'updateNumberOfMessagesProcessed': + self.emit(SIGNAL("updateNumberOfMessagesProcessed()")) + elif command == 'updateNumberOfPubkeysProcessed': + self.emit(SIGNAL("updateNumberOfPubkeysProcessed()")) + elif command == 'updateNumberOfBroadcastsProcessed': + self.emit(SIGNAL("updateNumberOfBroadcastsProcessed()")) elif command == 'setStatusIcon': self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data) elif command == 'rerenderInboxFromLabels': diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 6adfe490..19df1bdc 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -256,6 +256,7 @@ class receiveDataThread(threading.Thread): def connectionFullyEstablished(self): self.connectionIsOrWasFullyEstablished = True if not self.initiatedConnection: + shared.clientHasReceivedIncomingConnections = True shared.UISignalQueue.put(('setStatusIcon', 'green')) self.sock.settimeout( 600) # We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately. @@ -392,8 +393,9 @@ class receiveDataThread(threading.Thread): objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) + shared.numberOfBroadcastsProcessed += 1 shared.UISignalQueue.put(( - 'incrementNumberOfBroadcastsProcessed', 'no data')) + 'updateNumberOfBroadcastsProcessed', 'no data')) self.processbroadcast( readPosition, data) # When this function returns, we will have either successfully processed this broadcast because we are interested in it, ignored it because we aren't interested in it, or found problem with the broadcast that warranted ignoring it. @@ -760,8 +762,9 @@ class receiveDataThread(threading.Thread): objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) + shared.numberOfMessagesProcessed += 1 shared.UISignalQueue.put(( - 'incrementNumberOfMessagesProcessed', 'no data')) + 'updateNumberOfMessagesProcessed', 'no data')) self.processmsg( readPosition, data) # When this function returns, we will have either successfully processed the message bound for us, ignored it because it isn't bound for us, or found problem with the message that warranted ignoring it. @@ -1178,8 +1181,9 @@ class receiveDataThread(threading.Thread): objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(inventoryHash) + shared.numberOfPubkeysProcessed += 1 shared.UISignalQueue.put(( - 'incrementNumberOfPubkeysProcessed', 'no data')) + 'updateNumberOfPubkeysProcessed', 'no data')) self.processpubkey(data) diff --git a/src/shared.py b/src/shared.py index d2b82d11..fb19da26 100644 --- a/src/shared.py +++ b/src/shared.py @@ -64,6 +64,10 @@ successfullyDecryptMessageTimings = [ apiAddressGeneratorReturnQueue = Queue.Queue( ) # The address generator thread uses this queue to get information back to the API thread. ackdataForWhichImWatching = {} +clientHasReceivedIncomingConnections = False #used by API command clientStatus +numberOfMessagesProcessed = 0 +numberOfBroadcastsProcessed = 0 +numberOfPubkeysProcessed = 0 #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. From 82db79ca397aeecf9a9ed2707ca5d9b12dec1e1b Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 24 Aug 2013 20:23:49 -0400 Subject: [PATCH 34/86] removed option from previous commit which allowed user-settable maximum network message size pending further discussion --- src/class_receiveDataThread.py | 13 +------------ src/helper_startup.py | 4 ---- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index f014aedd..19df1bdc 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -45,17 +45,6 @@ class receiveDataThread(threading.Thread): self.peer = shared.Peer(HOST, port) self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header - self.maxMessageLength = 180000000 # maximum length of a message in bytes, default 180MB - - # get the maximum message length from the settings - try: - maxMsgLen = shared.config.getint( - 'bitmessagesettings', 'maxmessagelength') - if maxMsgLen > 32768: # minimum of 32K - self.maxMessageLength = maxMsgLen - except Exception: - pass - self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} self.selfInitiatedConnections = selfInitiatedConnections shared.connectedHostsList[ @@ -149,7 +138,7 @@ class receiveDataThread(threading.Thread): shared.knownNodesLock.acquire() shared.knownNodes[self.streamNumber][self.peer] = int(time.time()) shared.knownNodesLock.release() - if self.payloadLength <= self.maxMessageLength: # If the size of the message is greater than the maximum, ignore it. + if self.payloadLength <= 180000000: # If the size of the message is greater than 180MB, ignore it. (I get memory errors when processing messages much larger than this though it is concievable that this value will have to be lowered if some systems are less tolarant of large messages.) remoteCommand = self.data[4:16] with shared.printLock: print 'remoteCommand', repr(remoteCommand.replace('\x00', '')), ' from', self.peer diff --git a/src/helper_startup.py b/src/helper_startup.py index 8b4c2111..256dbcaa 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -69,11 +69,7 @@ def loadConfig(): shared.config.set( 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'dontconnect', 'true') -<<<<<<< HEAD ensureNamecoinOptions() -======= - shared.config.set('bitmessagesettings', 'maxMessageLength', '180000000') ->>>>>>> 3ff76875aa5d8b8dbadef48e28cc7e919b9042b3 if storeConfigFilesInSameDirectoryAsProgramByDefault: # Just use the same directory as the program and forget about From f0557e3987dab9c4b2ed34ada3e368be29b89b90 Mon Sep 17 00:00:00 2001 From: Rob Speed Date: Sun, 25 Aug 2013 04:36:43 -0400 Subject: [PATCH 35/86] Added Sip and PyQt to includes This should make it possible to distribute a DMG file. --- src/build_osx.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/build_osx.py b/src/build_osx.py index 901d1ca6..fd1bbcf9 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -5,12 +5,15 @@ version = "0.3.4" mainscript = ["bitmessagemain.py"] setup( - name = name, + name = name, version = version, app = mainscript, setup_requires = ["py2app"], - options = dict(py2app=dict( - resources = ["images"], - iconfile = "images/bitmessage.icns" - )) + options = dict( + py2app = dict( + resources = ["images"], + includes = ['sip', 'PyQt4._qt'], + iconfile = "images/bitmessage.icns" + ) + ) ) From f0ba10e2b9275325918e932603ca5d32c4ad6217 Mon Sep 17 00:00:00 2001 From: Rob Speed Date: Sun, 25 Aug 2013 04:43:32 -0400 Subject: [PATCH 36/86] Removed sudo when building DMG There's no reason that needs to be run as a superuser. --- osx.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 osx.sh diff --git a/osx.sh b/osx.sh old mode 100644 new mode 100755 index e5d2e371..2f59f446 --- a/osx.sh +++ b/osx.sh @@ -12,12 +12,12 @@ if [[ -z "$1" ]]; then exit fi -echo "Creating OS X packages for Bitmessage. This script will ask for sudo to create the dmg volume" +echo "Creating OS X packages for Bitmessage." cd src && python build_osx.py py2app if [[ $? = "0" ]]; then - sudo hdiutil create -fs HFS+ -volname "Bitmessage" -srcfolder dist/Bitmessage.app dist/bitmessage-v$1.dmg + hdiutil create -fs HFS+ -volname "Bitmessage" -srcfolder dist/Bitmessage.app dist/bitmessage-v$1.dmg else echo "Problem creating Bitmessage.app, stopping." exit From ea54f8e77961b9f01333cfc0fe33bc115db878a2 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 25 Aug 2013 16:23:28 -0400 Subject: [PATCH 37/86] resolve merge conflict --- src/bitmessagemain.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index da4e0297..494814d3 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -38,21 +38,17 @@ from class_singleWorker import * from class_outgoingSynSender import * from class_singleListener import * from class_addressGenerator import * +from debug import logger # Helper Functions import helper_bootstrap -<<<<<<< HEAD -======= -from debug import logger - import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: logger.critical("You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info)) sys.exit(0) ->>>>>>> 53ca5b03ffb6b35ac03588107dc4a1d5cde7aad4 def connectToStream(streamNumber): selfInitiatedConnections[streamNumber] = {} if sys.platform[0:3] == 'win': From 1b5158d658f426cbd8bdf8bad28d9a942d069e1a Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 25 Aug 2013 18:55:53 -0400 Subject: [PATCH 38/86] refactored helper_startup.py so that it can make use of a pre-set shared.appdata variable --- src/helper_startup.py | 155 +++++++++++++++++++++++------------------- src/namecoin.py | 2 +- 2 files changed, 85 insertions(+), 72 deletions(-) diff --git a/src/helper_startup.py b/src/helper_startup.py index 256dbcaa..2ce4ff2c 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -8,79 +8,92 @@ from namecoin import ensureNamecoinOptions storeConfigFilesInSameDirectoryAsProgramByDefault = False # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder. def loadConfig(): - # First try to load the config file (the keys.dat file) from the program - # directory - shared.config.read('keys.dat') - try: - shared.config.get('bitmessagesettings', 'settingsversion') - print 'Loading config files from same directory as program' - shared.appdata = '' - except: - # Could not load the keys.dat file in the program directory. Perhaps it - # is in the appdata directory. - shared.appdata = shared.lookupAppdataFolder() - shared.config = ConfigParser.SafeConfigParser() + if shared.appdata: shared.config.read(shared.appdata + 'keys.dat') + #shared.appdata must have been specified as a startup option. try: shared.config.get('bitmessagesettings', 'settingsversion') - print 'Loading existing config files from', shared.appdata + print 'Loading config files from directory specified on startup: ' + shared.appdata + needToCreateKeysFile = False except: - # This appears to be the first time running the program; there is - # no config file (or it cannot be accessed). Create config file. - shared.config.add_section('bitmessagesettings') - shared.config.set('bitmessagesettings', 'settingsversion', '6') - shared.config.set('bitmessagesettings', 'port', '8444') - shared.config.set( - 'bitmessagesettings', 'timeformat', '%%a, %%d %%b %%Y %%I:%%M %%p') - shared.config.set('bitmessagesettings', 'blackwhitelist', 'black') - shared.config.set('bitmessagesettings', 'startonlogon', 'false') - if 'linux' in sys.platform: - shared.config.set( - 'bitmessagesettings', 'minimizetotray', 'false') - # This isn't implimented yet and when True on - # Ubuntu causes Bitmessage to disappear while - # running when minimized. - else: - shared.config.set( - 'bitmessagesettings', 'minimizetotray', 'true') - shared.config.set( - 'bitmessagesettings', 'showtraynotifications', 'true') - shared.config.set('bitmessagesettings', 'startintray', 'false') - shared.config.set('bitmessagesettings', 'socksproxytype', 'none') - shared.config.set( - 'bitmessagesettings', 'sockshostname', 'localhost') - shared.config.set('bitmessagesettings', 'socksport', '9050') - shared.config.set( - 'bitmessagesettings', 'socksauthentication', 'false') - shared.config.set( - 'bitmessagesettings', 'sockslisten', 'false') - shared.config.set('bitmessagesettings', 'socksusername', '') - shared.config.set('bitmessagesettings', 'sockspassword', '') - shared.config.set('bitmessagesettings', 'keysencrypted', 'false') - shared.config.set( - 'bitmessagesettings', 'messagesencrypted', 'false') - shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str( - shared.networkDefaultProofOfWorkNonceTrialsPerByte)) - shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str( - shared.networkDefaultPayloadLengthExtraBytes)) - shared.config.set('bitmessagesettings', 'minimizeonclose', 'false') - shared.config.set( - 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte', '0') - shared.config.set( - 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') - shared.config.set('bitmessagesettings', 'dontconnect', 'true') - ensureNamecoinOptions() + needToCreateKeysFile = True - if storeConfigFilesInSameDirectoryAsProgramByDefault: - # Just use the same directory as the program and forget about - # the appdata folder - shared.appdata = '' - print 'Creating new config files in same directory as program.' - else: - print 'Creating new config files in', shared.appdata - if not os.path.exists(shared.appdata): - os.makedirs(shared.appdata) - if not sys.platform.startswith('win'): - os.umask(0o077) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + else: + shared.config.read('keys.dat') + try: + shared.config.get('bitmessagesettings', 'settingsversion') + print 'Loading config files from same directory as program.' + needToCreateKeysFile = False + shared.appdata = '' + except: + # Could not load the keys.dat file in the program directory. Perhaps it + # is in the appdata directory. + shared.appdata = shared.lookupAppdataFolder() + shared.config.read(shared.appdata + 'keys.dat') + try: + shared.config.get('bitmessagesettings', 'settingsversion') + print 'Loading existing config files from', shared.appdata + needToCreateKeysFile = False + except: + needToCreateKeysFile = True + + if needToCreateKeysFile: + # This appears to be the first time running the program; there is + # no config file (or it cannot be accessed). Create config file. + shared.config.add_section('bitmessagesettings') + shared.config.set('bitmessagesettings', 'settingsversion', '6') + shared.config.set('bitmessagesettings', 'port', '8444') + shared.config.set( + 'bitmessagesettings', 'timeformat', '%%a, %%d %%b %%Y %%I:%%M %%p') + shared.config.set('bitmessagesettings', 'blackwhitelist', 'black') + shared.config.set('bitmessagesettings', 'startonlogon', 'false') + if 'linux' in sys.platform: + shared.config.set( + 'bitmessagesettings', 'minimizetotray', 'false') + # This isn't implimented yet and when True on + # Ubuntu causes Bitmessage to disappear while + # running when minimized. + else: + shared.config.set( + 'bitmessagesettings', 'minimizetotray', 'true') + shared.config.set( + 'bitmessagesettings', 'showtraynotifications', 'true') + shared.config.set('bitmessagesettings', 'startintray', 'false') + shared.config.set('bitmessagesettings', 'socksproxytype', 'none') + shared.config.set( + 'bitmessagesettings', 'sockshostname', 'localhost') + shared.config.set('bitmessagesettings', 'socksport', '9050') + shared.config.set( + 'bitmessagesettings', 'socksauthentication', 'false') + shared.config.set( + 'bitmessagesettings', 'sockslisten', 'false') + shared.config.set('bitmessagesettings', 'socksusername', '') + shared.config.set('bitmessagesettings', 'sockspassword', '') + shared.config.set('bitmessagesettings', 'keysencrypted', 'false') + shared.config.set( + 'bitmessagesettings', 'messagesencrypted', 'false') + shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str( + shared.networkDefaultProofOfWorkNonceTrialsPerByte)) + shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str( + shared.networkDefaultPayloadLengthExtraBytes)) + shared.config.set('bitmessagesettings', 'minimizeonclose', 'false') + shared.config.set( + 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte', '0') + shared.config.set( + 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') + shared.config.set('bitmessagesettings', 'dontconnect', 'true') + ensureNamecoinOptions() + + if storeConfigFilesInSameDirectoryAsProgramByDefault: + # Just use the same directory as the program and forget about + # the appdata folder + shared.appdata = '' + print 'Creating new config files in same directory as program.' + else: + print 'Creating new config files in', shared.appdata + if not os.path.exists(shared.appdata): + os.makedirs(shared.appdata) + if not sys.platform.startswith('win'): + os.umask(0o077) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) diff --git a/src/namecoin.py b/src/namecoin.py index 9fbf4518..f9565ccf 100644 --- a/src/namecoin.py +++ b/src/namecoin.py @@ -270,7 +270,7 @@ def ensureNamecoinOptions (): nmc.close () except Exception as exc: - print "Failure reading namecoin config file: %s" % str (exc) + print "Could not read the Namecoin config file probably because you don't have Namecoin installed. That's ok; we don't really need it. Detailed error message: %s" % str (exc) # If still nothing found, set empty at least. if (not hasUser): From 4396bc7f97bb27e350f8a7ee4211bffb95c4f43f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 25 Aug 2013 19:31:54 -0400 Subject: [PATCH 39/86] manually undid much of pull #287. Discussion in #398 --- src/bitmessagemain.py | 7 --- src/bitmessageqt/__init__.py | 119 ++++++++++++++--------------------- src/class_bgWorker.py | 38 ----------- 3 files changed, 46 insertions(+), 118 deletions(-) delete mode 100644 src/class_bgWorker.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 494814d3..be962bc5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -9,13 +9,6 @@ # The software version variable is now held in shared.py -# import ctypes -try: - from gevent import monkey - monkey.patch_all() -except ImportError as ex: - print "Not using the gevent module as it was not found. No need to worry." - import signal # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. # The next 3 are used for the API from SimpleXMLRPCServer import * diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 00ac8bde..011b7b54 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3333,80 +3333,57 @@ class myTableWidgetItem(QTableWidgetItem): def __lt__(self, other): return int(self.data(33).toPyObject()) < int(other.data(33).toPyObject()) -from threading import Thread -class UISignaler(Thread,QThread): +class UISignaler(QThread): def __init__(self, parent=None): - Thread.__init__(self, parent) QThread.__init__(self, parent) def run(self): while True: - try: - command, data = shared.UISignalQueue.get() - if command == 'writeNewAddressToTable': - label, address, streamNumber = data - self.emit(SIGNAL( - "writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), label, address, str(streamNumber)) - elif command == 'updateStatusBar': - self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"), data) - elif command == 'updateSentItemStatusByHash': - hash, message = data - self.emit(SIGNAL( - "updateSentItemStatusByHash(PyQt_PyObject,PyQt_PyObject)"), hash, message) - elif command == 'updateSentItemStatusByAckdata': - ackData, message = data - self.emit(SIGNAL( - "updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), ackData, message) - elif command == 'displayNewInboxMessage': - inventoryHash, toAddress, fromAddress, subject, body = data - self.emit(SIGNAL( - "displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), - inventoryHash, toAddress, fromAddress, subject, body) - elif command == 'displayNewSentMessage': - toAddress, fromLabel, fromAddress, subject, message, ackdata = data - self.emit(SIGNAL( - "displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), - toAddress, fromLabel, fromAddress, subject, message, ackdata) - elif command == 'updateNetworkStatusTab': - self.emit(SIGNAL("updateNetworkStatusTab()")) - elif command == 'updateNumberOfMessagesProcessed': - self.emit(SIGNAL("updateNumberOfMessagesProcessed()")) - elif command == 'updateNumberOfPubkeysProcessed': - self.emit(SIGNAL("updateNumberOfPubkeysProcessed()")) - elif command == 'updateNumberOfBroadcastsProcessed': - self.emit(SIGNAL("updateNumberOfBroadcastsProcessed()")) - elif command == 'setStatusIcon': - self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data) - elif command == 'rerenderInboxFromLabels': - self.emit(SIGNAL("rerenderInboxFromLabels()")) - elif command == 'rerenderSubscriptions': - self.emit(SIGNAL("rerenderSubscriptions()")) - elif command == 'removeInboxRowByMsgid': - self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data) - else: - sys.stderr.write( - 'Command sent to UISignaler not recognized: %s\n' % command) - except Exception,ex: - # uncaught exception will block gevent - import traceback - traceback.print_exc() - traceback.print_stack() - print ex - pass - -try: - import gevent -except ImportError as ex: - gevent = None -else: - def mainloop(app): - while True: - app.processEvents() - gevent.sleep(0.01) - def testprint(): - #print 'this is running' - gevent.spawn_later(1, testprint) + command, data = shared.UISignalQueue.get() + if command == 'writeNewAddressToTable': + label, address, streamNumber = data + self.emit(SIGNAL( + "writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), label, address, str(streamNumber)) + elif command == 'updateStatusBar': + self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"), data) + elif command == 'updateSentItemStatusByHash': + hash, message = data + self.emit(SIGNAL( + "updateSentItemStatusByHash(PyQt_PyObject,PyQt_PyObject)"), hash, message) + elif command == 'updateSentItemStatusByAckdata': + ackData, message = data + self.emit(SIGNAL( + "updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), ackData, message) + elif command == 'displayNewInboxMessage': + inventoryHash, toAddress, fromAddress, subject, body = data + self.emit(SIGNAL( + "displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), + inventoryHash, toAddress, fromAddress, subject, body) + elif command == 'displayNewSentMessage': + toAddress, fromLabel, fromAddress, subject, message, ackdata = data + self.emit(SIGNAL( + "displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), + toAddress, fromLabel, fromAddress, subject, message, ackdata) + elif command == 'updateNetworkStatusTab': + self.emit(SIGNAL("updateNetworkStatusTab()")) + elif command == 'updateNumberOfMessagesProcessed': + self.emit(SIGNAL("updateNumberOfMessagesProcessed()")) + elif command == 'updateNumberOfPubkeysProcessed': + self.emit(SIGNAL("updateNumberOfPubkeysProcessed()")) + elif command == 'updateNumberOfBroadcastsProcessed': + self.emit(SIGNAL("updateNumberOfBroadcastsProcessed()")) + elif command == 'setStatusIcon': + self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data) + elif command == 'rerenderInboxFromLabels': + self.emit(SIGNAL("rerenderInboxFromLabels()")) + elif command == 'rerenderSubscriptions': + self.emit(SIGNAL("rerenderSubscriptions()")) + elif command == 'removeInboxRowByMsgid': + self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data) + else: + sys.stderr.write( + 'Command sent to UISignaler not recognized: %s\n' % command) def run(): app = QtGui.QApplication(sys.argv) @@ -3443,8 +3420,4 @@ def run(): myapp.notifierInit() if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): myapp.showConnectDialog() # ask the user if we may connect - if gevent is None: - sys.exit(app.exec_()) - else: - gevent.joinall([gevent.spawn(testprint), gevent.spawn(mainloop, app), gevent.spawn(mainloop, app), gevent.spawn(mainloop, app), gevent.spawn(mainloop, app), gevent.spawn(mainloop, app)]) - print 'done' + sys.exit(app.exec_()) diff --git a/src/class_bgWorker.py b/src/class_bgWorker.py deleted file mode 100644 index c01e26ef..00000000 --- a/src/class_bgWorker.py +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/python -# -*- coding: utf-8 -*- -# cody by linker.lin@me.com - -__author__ = 'linkerlin' - - -import threading -import Queue -import time - -class bgWorker(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.q = Queue.Queue() - self.setDaemon(True) - - def post(self,job): - self.q.put(job) - - def run(self): - while 1: - job=None - try: - job = self.q.get(block=True) - if job: - job() - except Exception as ex: - print "Error,job exception:",ex.message,type(ex) - time.sleep(0.05) - else: - #print "job: ", job, " done" - pass - finally: - time.sleep(0.05) - -bgworker = bgWorker() -bgworker.start() From 14a968b499075805d7d9fd2fb42f784d413cb55b Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 25 Aug 2013 22:52:38 -0400 Subject: [PATCH 40/86] github demanded a manual merge --- src/bitmessagemain.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index bc884121..662ca7a6 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -875,8 +875,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): except APIError as e: return str(e) except Exception as e: - print(e) - print(sys.exc_info()[0]) + logger.critical(e) + logger.critical(sys.exc_info()[0]) return "API Error 0021: Unexpected API Failure - %s" % str(e) # This thread, of which there is only one, runs the API. @@ -988,7 +988,6 @@ class Main: def getApiAddress(self): if not shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): return None - address = shared.config.get('bitmessagesettings', 'apiinterface') port = shared.config.getint('bitmessagesettings', 'apiport') return {'address':address,'port':port} From 3ae8dd8eeef36091d5e0d3c1e750833191a0a91f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 26 Aug 2013 00:06:49 -0400 Subject: [PATCH 41/86] log traceback on API exception --- src/bitmessagemain.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 662ca7a6..721b6a8d 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -875,8 +875,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): except APIError as e: return str(e) except Exception as e: - logger.critical(e) - logger.critical(sys.exc_info()[0]) + logger.exception(e) return "API Error 0021: Unexpected API Failure - %s" % str(e) # This thread, of which there is only one, runs the API. From 732d7c999aa968456f819cb83ef6d978992b8a19 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 26 Aug 2013 08:44:15 -0400 Subject: [PATCH 42/86] Allow specification of alternate settings dir via BITMESSAGE_HOME --- src/shared.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index fb19da26..e39121d2 100644 --- a/src/shared.py +++ b/src/shared.py @@ -126,7 +126,11 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): def lookupAppdataFolder(): APPNAME = "PyBitmessage" from os import path, environ - if sys.platform == 'darwin': + if "BITMESSAGE_HOME" in environ: + dataFolder = environ["BITMESSAGE_HOME"] + if dataFolder[-1] not in [os.path.sep, os.path.altsep]: + dataFolder += os.path.sep + elif sys.platform == 'darwin': if "HOME" in environ: dataFolder = path.join(os.environ["HOME"], "Library/Application Support/", APPNAME) + '/' else: From b5f42d75496f9119070fe679fb096ddb20b374df Mon Sep 17 00:00:00 2001 From: Joshua Noble Date: Mon, 26 Aug 2013 22:29:57 -0400 Subject: [PATCH 43/86] Added trashSentMessageByAckData API command --- src/bitmessagemain.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 721b6a8d..e18a15d3 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -507,6 +507,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.sqlLock.release() # shared.UISignalQueue.put(('removeSentRowByMsgid',msgid)) This function doesn't exist yet. return 'Trashed sent message (assuming message existed).' + elif method == 'trashSentMessageByAckData': + # This API method should only be used when msgid is not available + if len(params) == 0: + raise APIError(0, 'I need parameters!') + ackdata = self._decode(params[0], "hex") + t = (ackdata,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE ackdata=?''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + return 'Trashed sent message (assuming message existed).' elif method == 'sendMessage': if len(params) == 0: raise APIError(0, 'I need parameters!') From edf9101eae52ce0a3f87b8f233e3f8ba1dcbbee0 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 26 Aug 2013 20:00:30 -0400 Subject: [PATCH 44/86] Move duplicated sql code into helper --- src/helper_sql.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/helper_sql.py diff --git a/src/helper_sql.py b/src/helper_sql.py new file mode 100644 index 00000000..f32a31d4 --- /dev/null +++ b/src/helper_sql.py @@ -0,0 +1,33 @@ +import shared + +def sqlQuery(sqlStatement, *args): + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put(sqlStatement) + + if args == (): + shared.sqlSubmitQueue.put('') + else: + shared.sqlSubmitQueue.put(args) + + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + + return queryreturn + +def sqlExecute(sqlStatement, *args): + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put(sqlStatement) + + if args == (): + shared.sqlSubmitQueue.put('') + else: + shared.sqlSubmitQueue.put(args) + + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + +def sqlStoredProcedure(procName): + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put(procName) + shared.sqlLock.release() From 74cd6c24b2216abafae2f045bd76302638b96e84 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 26 Aug 2013 20:00:53 -0400 Subject: [PATCH 45/86] Have API calls use sql helper --- src/bitmessagemain.py | 163 +++++++++--------------------------------- 1 file changed, 34 insertions(+), 129 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 721b6a8d..c6b4c6b8 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -25,6 +25,7 @@ if sys.platform == 'darwin': sys.exit(0) # Classes +from helper_sql import * from class_sqlThread import * from class_singleCleaner import * from class_singleWorker import * @@ -315,12 +316,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) return shared.apiAddressGeneratorReturnQueue.get() elif method == 'getAllInboxMessages': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() data = '{"inboxMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row @@ -333,12 +330,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data elif method == 'getAllInboxMessageIds' or method == 'getAllInboxMessageIDs': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() data = '{"inboxMessageIds":[' for row in queryreturn: msgid = row[0] @@ -351,12 +344,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") - v = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) data = '{"inboxMessage":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row @@ -366,11 +354,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data elif method == 'getAllSentMessages': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') data = '{"sentMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row @@ -382,11 +366,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data elif method == 'getAllSentMessageIds' or method == 'getAllSentMessageIDs': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') data = '{"sentMessageIds":[' for row in queryreturn: msgid = row[0] @@ -399,12 +379,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') toAddress = params[0] - v = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryReturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?''', toAddress) data = '{"inboxMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, encodingtype = row @@ -419,12 +394,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") - v = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''', msgid) data = '{"sentMessage":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row @@ -437,12 +407,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') fromAddress = params[0] - v = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime''', + fromAddress) data = '{"sentMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row @@ -457,12 +423,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') ackData = self._decode(params[0], "hex") - v = (ackData,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''', + ackData) data = '{"sentMessage":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row @@ -480,13 +442,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): helper_inbox.trash(msgid) # Trash if in sent table t = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - # shared.UISignalQueue.put(('removeSentRowByMsgid',msgid)) This function doesn't exist yet. + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', t) return 'Trashed message (assuming message existed).' elif method == 'trashInboxMessage': if len(params) == 0: @@ -499,13 +455,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") t = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - # shared.UISignalQueue.put(('removeSentRowByMsgid',msgid)) This function doesn't exist yet. + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', t) return 'Trashed sent message (assuming message existed).' elif method == 'sendMessage': if len(params) == 0: @@ -568,13 +518,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): helper_sent.insert(t) toLabel = '' - t = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''select label from addressbook where address=?''', toAddress) if queryreturn != []: for row in queryreturn: toLabel, = row @@ -643,12 +587,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(ackdata) != 64: raise APIError(15, 'The length of ackData should be 32 bytes (encoded in hex thus 64 characters).') ackdata = self._decode(ackdata, "hex") - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT status FROM sent where ackdata=?''') - shared.sqlSubmitQueue.put((ackdata,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT status FROM sent where ackdata=?''', + ackdata) if queryreturn == []: return 'notfound' for row in queryreturn: @@ -688,23 +629,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(12, 'The stream number must be 1. Others aren\'t supported.') # First we must check to see if the address is already in the # subscriptions list. - shared.sqlLock.acquire() - t = (address,) - shared.sqlSubmitQueue.put( - '''select * from subscriptions where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''select * from subscriptions where address=?''', address) if queryreturn != []: raise APIError(16, 'You are already subscribed to that address.') - t = (label, address, True) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO subscriptions VALUES (?,?,?)''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',label, address, True) shared.reloadBroadcastSendersForWhichImWatching() shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) shared.UISignalQueue.put(('rerenderSubscriptions', '')) @@ -715,24 +643,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(0, 'I need 1 parameter!') address, = params address = addBMIfNotPresent(address) - t = (address,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''DELETE FROM subscriptions WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address) shared.reloadBroadcastSendersForWhichImWatching() shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) shared.UISignalQueue.put(('rerenderSubscriptions', '')) return 'Deleted subscription if it existed.' elif method == 'listSubscriptions': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT label, address, enabled FROM subscriptions''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT label, address, enabled FROM subscriptions''') data = '{"subscriptions":[' for row in queryreturn: label, address, enabled = row @@ -803,26 +720,18 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # use it we'll need to fill out a field in our inventory database # which is blank by default (first20bytesofencryptedmessage). parameters = '' - with shared.sqlLock: - shared.sqlSubmitQueue.put('''SELECT hash, payload FROM inventory WHERE first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''') - shared.sqlSubmitQueue.put(parameters) - queryreturn = shared.sqlReturnQueue.get() - - for row in queryreturn: - hash, payload = row - readPosition = 16 # Nonce length + time length - readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length - t = (payload[readPosition:readPosition+20],hash) - shared.sqlSubmitQueue.put('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() + queryreturn = sqlQuery( + '''SELECT hash, payload FROM inventory WHERE first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''') + + for row in queryreturn: + hash, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+20],hash) + sqlExecute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', t) - parameters = (requestedHash,) - with shared.sqlLock: - shared.sqlSubmitQueue.put('commit') - shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''') - shared.sqlSubmitQueue.put(parameters) - queryreturn = shared.sqlReturnQueue.get() + queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''', + requestedHash) data = '{"receivedMessageDatas":[' for row in queryreturn: payload, = row @@ -840,11 +749,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(requestedHash) != 40: raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).') requestedHash = self._decode(requestedHash, "hex") - parameters = (requestedHash,) - with shared.sqlLock: - shared.sqlSubmitQueue.put('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''') - shared.sqlSubmitQueue.put(parameters) - queryreturn = shared.sqlReturnQueue.get() + queryreturn = sqlQuery('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''', requestedHash) data = '{"pubkey":[' for row in queryreturn: transmitdata, = row From 7a53d2950b5285e0dc170e604bb00c0dabfa0f85 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 26 Aug 2013 20:01:19 -0400 Subject: [PATCH 46/86] Have bitmessageqt use sql helpers --- src/bitmessageqt/__init__.py | 433 +++++++++-------------------------- 1 file changed, 106 insertions(+), 327 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 011b7b54..f21f4ecc 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -36,6 +36,7 @@ import debug from debug import logger import subprocess import datetime +from helper_sql import * try: from PyQt4 import QtCore, QtGui @@ -346,11 +347,7 @@ class MyForm(QtGui.QMainWindow): self.loadSent() # Initialize the address book - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('SELECT * FROM addressbook') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('SELECT * FROM addressbook') for row in queryreturn: label, address = row self.ui.tableWidgetAddressBook.insertRow(0) @@ -561,7 +558,7 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - sqlQuery = ''' + sql = ''' SELECT toaddress, fromaddress, subject, message, status, ackdata, lastactiontime FROM sent WHERE folder="sent" AND %s LIKE ? ORDER BY lastactiontime @@ -570,12 +567,7 @@ class MyForm(QtGui.QMainWindow): while self.ui.tableWidgetSent.rowCount() > 0: self.ui.tableWidgetSent.removeRow(0) - t = (what,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put(sqlQuery) - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery(sql, what) for row in queryreturn: toAddress, fromAddress, subject, message, status, ackdata, lastactiontime = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -588,13 +580,8 @@ class MyForm(QtGui.QMainWindow): fromLabel = fromAddress toLabel = '' - t = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', toAddress) if queryreturn != []: for row in queryreturn: @@ -691,7 +678,7 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - sqlQuery = ''' + sql = ''' SELECT msgid, toaddress, fromaddress, subject, received, message, read FROM inbox WHERE folder="inbox" AND %s LIKE ? ORDER BY received @@ -702,12 +689,7 @@ class MyForm(QtGui.QMainWindow): font = QFont() font.setBold(True) - t = (what,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put(sqlQuery) - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery(sql, what) for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, read = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -723,26 +705,16 @@ class MyForm(QtGui.QMainWindow): toLabel = toAddress fromLabel = '' - t = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', fromAddress) if queryreturn != []: for row in queryreturn: fromLabel, = row if fromLabel == '': # If this address wasn't in our address book... - t = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from subscriptions where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryReturn = sqlQuery( + '''select label from subscriptions where address=?''', fromAddress) if queryreturn != []: for row in queryreturn: @@ -883,12 +855,8 @@ class MyForm(QtGui.QMainWindow): if not (self.mmapp.has_source("Subscriptions") or self.mmapp.has_source("Messages")): return - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT toaddress, read FROM inbox WHERE msgid=?''') - shared.sqlSubmitQueue.put(inventoryHash) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT toaddress, read FROM inbox WHERE msgid=?''', inventoryHash) for row in queryreturn: toAddress, read = row if not read: @@ -904,12 +872,8 @@ class MyForm(QtGui.QMainWindow): unreadMessages = 0 unreadSubscriptions = 0 - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT msgid, toaddress, read FROM inbox where folder='inbox' ''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() for row in queryreturn: msgid, toAddress, read = row @@ -1156,9 +1120,7 @@ class MyForm(QtGui.QMainWindow): def click_actionDeleteAllTrashedMessages(self): if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: return - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('deleteandvacuume') - shared.sqlLock.release() + sqlStoredProcedure('deleteandvacuume') def click_actionRegenerateDeterministicAddresses(self): self.regenerateAddressesDialogInstance = regenerateAddressesDialog( @@ -1439,13 +1401,8 @@ class MyForm(QtGui.QMainWindow): addressToLookup = str(self.ui.tableWidgetInbox.item( i, 1).data(Qt.UserRole).toPyObject()) fromLabel = '' - t = (addressToLookup,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', addressToLookup) if queryreturn != []: for row in queryreturn: @@ -1455,12 +1412,8 @@ class MyForm(QtGui.QMainWindow): else: # It might be a broadcast message. We should check for that # label. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from subscriptions where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', addressToLookup) if queryreturn != []: for row in queryreturn: @@ -1506,13 +1459,8 @@ class MyForm(QtGui.QMainWindow): addressToLookup = str(self.ui.tableWidgetSent.item( i, 0).data(Qt.UserRole).toPyObject()) toLabel = '' - t = (addressToLookup,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', addressToLookup) if queryreturn != []: for row in queryreturn: @@ -1522,12 +1470,7 @@ class MyForm(QtGui.QMainWindow): def rerenderSubscriptions(self): self.ui.tableWidgetSubscriptions.setRowCount(0) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - 'SELECT label, address, enabled FROM subscriptions') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') for row in queryreturn: label, address, enabled = row self.ui.tableWidgetSubscriptions.insertRow(0) @@ -1610,24 +1553,26 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.")) ackdata = OpenSSL.rand(32) - shared.sqlLock.acquire() - t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( - time.time()), 'msgqueued', 1, 1, 'sent', 2) - shared.sqlSubmitQueue.put( - '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + t = () + sqlExecute( + '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''', + '', + toAddress, + ripe, + fromAddress, + subject, + message, + ackdata, + int(time.time()), + 'msgqueued', + 1, + 1, + 'sent', + 2) toLabel = '' - t = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''select label from addressbook where address=?''', + toAddress) if queryreturn != []: for row in queryreturn: toLabel, = row @@ -1658,15 +1603,10 @@ class MyForm(QtGui.QMainWindow): ackdata = OpenSSL.rand(32) toAddress = self.str_broadcast_subscribers ripe = '' - shared.sqlLock.acquire() t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( time.time()), 'broadcastqueued', 1, 1, 'sent', 2) - shared.sqlSubmitQueue.put( - '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''', t) shared.workerQueue.put(('sendbroadcast', '')) @@ -1822,25 +1762,15 @@ class MyForm(QtGui.QMainWindow): subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) fromLabel = '' - shared.sqlLock.acquire() - t = (fromAddress,) - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', fromAddress) if queryreturn != []: for row in queryreturn: fromLabel, = row else: # There might be a label in the subscriptions table - shared.sqlLock.acquire() - t = (fromAddress,) - shared.sqlSubmitQueue.put( - '''select label from subscriptions where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', fromAddress) if queryreturn != []: for row in queryreturn: fromLabel, = row @@ -1914,13 +1844,7 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "The address you entered was invalid. Ignoring it.")) def addEntryToAddressBook(self,address,label): - shared.sqlLock.acquire() - t = (address,) - shared.sqlSubmitQueue.put( - '''select * from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + sqlQuery('''select * from addressbook where address=?''', address) if queryreturn == []: self.ui.tableWidgetAddressBook.setSortingEnabled(False) self.ui.tableWidgetAddressBook.insertRow(0) @@ -1931,14 +1855,7 @@ class MyForm(QtGui.QMainWindow): QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) self.ui.tableWidgetAddressBook.setSortingEnabled(True) - t = (str(label), address) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO addressbook VALUES (?,?)''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(label), address) self.rerenderInboxFromLabels() self.rerenderSentToLabels() else: @@ -1960,13 +1877,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSubscriptions.setItem(0,1,newItem) self.ui.tableWidgetSubscriptions.setSortingEnabled(True) #Add to database (perhaps this should be separated from the MyForm class) - t = (str(label),address,True) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO subscriptions VALUES (?,?,?)''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() @@ -1987,16 +1898,10 @@ class MyForm(QtGui.QMainWindow): def loadBlackWhiteList(self): # Initialize the Blacklist or Whitelist table listType = shared.config.get('bitmessagesettings', 'blackwhitelist') - shared.sqlLock.acquire() if listType == 'black': - shared.sqlSubmitQueue.put( - '''SELECT label, address, enabled FROM blacklist''') + queryreturn = sqlQuery('''SELECT label, address, enabled FROM blacklist''') else: - shared.sqlSubmitQueue.put( - '''SELECT label, address, enabled FROM whitelist''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT label, address, enabled FROM whitelist''') for row in queryreturn: label, address, enabled = row self.ui.tableWidgetBlacklist.insertRow(0) @@ -2115,9 +2020,7 @@ class MyForm(QtGui.QMainWindow): if shared.appdata != '' and self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we are NOT using portable mode now but the user selected that we should... # Write the keys.dat file to disk in the new location - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('movemessagstoprog') - shared.sqlLock.release() + sqlStoredProcedure('movemessagstoprog') with open('keys.dat', 'wb') as configfile: shared.config.write(configfile) # Write the knownnodes.dat file to disk in the new location @@ -2141,9 +2044,7 @@ class MyForm(QtGui.QMainWindow): shared.appdata = shared.lookupAppdataFolder() if not os.path.exists(shared.appdata): os.makedirs(shared.appdata) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('movemessagstoappdata') - shared.sqlLock.release() + sqlStoredProcedure('movemessagstoappdata') # Write the keys.dat file to disk in the new location with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) @@ -2189,18 +2090,13 @@ class MyForm(QtGui.QMainWindow): # First we must check to see if the address is already in the # address book. The user cannot add it again or else it will # cause problems when updating and deleting the entry. - shared.sqlLock.acquire() t = (addBMIfNotPresent(str( self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())),) if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': - shared.sqlSubmitQueue.put( - '''select * from blacklist where address=?''') + sql = '''select * from blacklist where address=?''' else: - shared.sqlSubmitQueue.put( - '''select * from whitelist where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + sql = '''select * from whitelist where address=?''' + queryreturn = sqlQuery(sql,*t) if queryreturn == []: self.ui.tableWidgetBlacklist.setSortingEnabled(False) self.ui.tableWidgetBlacklist.insertRow(0) @@ -2215,17 +2111,11 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetBlacklist.setSortingEnabled(True) t = (str(self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8()), addBMIfNotPresent( str(self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())), True) - shared.sqlLock.acquire() if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': - shared.sqlSubmitQueue.put( - '''INSERT INTO blacklist VALUES (?,?,?)''') + sql = '''INSERT INTO blacklist VALUES (?,?,?)''' else: - shared.sqlSubmitQueue.put( - '''INSERT INTO whitelist VALUES (?,?,?)''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sql = '''INSERT INTO whitelist VALUES (?,?,?)''' + sqlExecute(sql, *t) else: self.statusBar().showMessage(_translate( "MainWindow", "Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want.")) @@ -2352,20 +2242,11 @@ class MyForm(QtGui.QMainWindow): currentRow = row.row() inventoryHashToMarkUnread = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - t = (inventoryHashToMarkUnread,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE inbox SET read=0 WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlLock.release() + sqlExecute('''UPDATE inbox SET read=0 WHERE msgid=?''', inventoryHashToMarkUnread) self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() # self.ui.tableWidgetInbox.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. @@ -2410,13 +2291,8 @@ class MyForm(QtGui.QMainWindow): addressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) # Let's make sure that it isn't already in the address book - shared.sqlLock.acquire() - t = (addressAtCurrentInboxRow,) - shared.sqlSubmitQueue.put( - '''select * from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''select * from addressbook where address=?''', + addressAtCurrentInboxRow) if queryreturn == []: self.ui.tableWidgetAddressBook.insertRow(0) newItem = QtGui.QTableWidgetItem( @@ -2426,15 +2302,9 @@ class MyForm(QtGui.QMainWindow): newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) - t = ('--New entry. Change label in Address Book.--', - addressAtCurrentInboxRow) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO addressbook VALUES (?,?)''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', + '--New entry. Change label in Address Book.--', + addressAtCurrentInboxRow) self.ui.tabWidget.setCurrentIndex(5) self.ui.tableWidgetAddressBook.setCurrentCell(0, 0) self.statusBar().showMessage(_translate( @@ -2449,20 +2319,11 @@ class MyForm(QtGui.QMainWindow): currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row() inventoryHashToTrash = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - t = (inventoryHashToTrash,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE inbox SET folder='trash' WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlLock.release() + sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) self.ui.textEditInboxMessage.setText("") self.ui.tableWidgetInbox.removeRow(currentRow) self.statusBar().showMessage(_translate( "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() if currentRow == 0: self.ui.tableWidgetInbox.selectRow(currentRow) else: @@ -2493,20 +2354,11 @@ class MyForm(QtGui.QMainWindow): currentRow = self.ui.tableWidgetSent.selectedIndexes()[0].row() ackdataToTrash = str(self.ui.tableWidgetSent.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - t = (ackdataToTrash,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET folder='trash' WHERE ackdata=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlLock.release() + sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) self.ui.textEditSentMessage.setPlainText("") self.ui.tableWidgetSent.removeRow(currentRow) self.statusBar().showMessage(_translate( "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() if currentRow == 0: self.ui.tableWidgetSent.selectRow(currentRow) else: @@ -2517,18 +2369,10 @@ class MyForm(QtGui.QMainWindow): addressAtCurrentRow = str(self.ui.tableWidgetSent.item( currentRow, 0).data(Qt.UserRole).toPyObject()) toRipe = decodeAddress(addressAtCurrentRow)[3] - t = (toRipe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlSubmitQueue.put( - '''select ackdata FROM sent WHERE status='forcepow' ''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''', + toRipe) + queryreturn = sqlQuery('''select ackdata FROM sent WHERE status='forcepow' ''') for row in queryreturn: ackdata, = row shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( @@ -2554,14 +2398,8 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).text().toUtf8() addressAtCurrentRow = self.ui.tableWidgetAddressBook.item( currentRow, 1).text() - t = (str(labelAtCurrentRow), str(addressAtCurrentRow)) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''DELETE FROM addressbook WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''DELETE FROM addressbook WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) self.ui.tableWidgetAddressBook.removeRow(currentRow) self.rerenderInboxFromLabels() self.rerenderSentToLabels() @@ -2631,14 +2469,8 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).text().toUtf8() addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 1).text() - t = (str(labelAtCurrentRow), str(addressAtCurrentRow)) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''DELETE FROM subscriptions WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''DELETE FROM subscriptions WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) self.ui.tableWidgetSubscriptions.removeRow(currentRow) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() @@ -2656,14 +2488,9 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).text().toUtf8() addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 1).text() - t = (str(labelAtCurrentRow), str(addressAtCurrentRow)) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''update subscriptions set enabled=1 WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''update subscriptions set enabled=1 WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) self.ui.tableWidgetSubscriptions.item( currentRow, 0).setTextColor(QApplication.palette().text().color()) self.ui.tableWidgetSubscriptions.item( @@ -2676,14 +2503,9 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).text().toUtf8() addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 1).text() - t = (str(labelAtCurrentRow), str(addressAtCurrentRow)) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''update subscriptions set enabled=0 WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''update subscriptions set enabled=0 WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) self.ui.tableWidgetSubscriptions.item( currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetSubscriptions.item( @@ -2704,20 +2526,14 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).text().toUtf8() addressAtCurrentRow = self.ui.tableWidgetBlacklist.item( currentRow, 1).text() - t = (str(labelAtCurrentRow), str(addressAtCurrentRow)) - shared.sqlLock.acquire() if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': - shared.sqlSubmitQueue.put( - '''DELETE FROM blacklist WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() + sqlExecute( + '''DELETE FROM blacklist WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) else: - shared.sqlSubmitQueue.put( - '''DELETE FROM whitelist WHERE label=? AND address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''DELETE FROM whitelist WHERE label=? AND address=?''', + str(labelAtCurrentRow), str(addressAtCurrentRow)) self.ui.tableWidgetBlacklist.removeRow(currentRow) def on_action_BlacklistClipboard(self): @@ -2739,20 +2555,14 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).setTextColor(QApplication.palette().text().color()) self.ui.tableWidgetBlacklist.item( currentRow, 1).setTextColor(QApplication.palette().text().color()) - t = (str(addressAtCurrentRow),) - shared.sqlLock.acquire() if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': - shared.sqlSubmitQueue.put( - '''UPDATE blacklist SET enabled=1 WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() + sqlExecute( + '''UPDATE blacklist SET enabled=1 WHERE address=?''', + str(addressAtCurrentRow)) else: - shared.sqlSubmitQueue.put( - '''UPDATE whitelist SET enabled=1 WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE whitelist SET enabled=1 WHERE address=?''', + str(addressAtCurrentRow)) def on_action_BlacklistDisable(self): currentRow = self.ui.tableWidgetBlacklist.currentRow() @@ -2762,20 +2572,12 @@ class MyForm(QtGui.QMainWindow): currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetBlacklist.item( currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) - t = (str(addressAtCurrentRow),) - shared.sqlLock.acquire() if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': - shared.sqlSubmitQueue.put( - '''UPDATE blacklist SET enabled=0 WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() + sqlExecute( + '''UPDATE blacklist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow)) else: - shared.sqlSubmitQueue.put( - '''UPDATE whitelist SET enabled=0 WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow)) # Group of functions for the Your Identities dialog box def on_action_YourIdentitiesNew(self): @@ -2841,12 +2643,7 @@ class MyForm(QtGui.QMainWindow): currentRow = self.ui.tableWidgetSent.currentRow() ackData = str(self.ui.tableWidgetSent.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT status FROM sent where ackdata=?''') - shared.sqlSubmitQueue.put((ackData,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData) for row in queryreturn: status, = row if status == 'toodifficult': @@ -2903,13 +2700,7 @@ class MyForm(QtGui.QMainWindow): currentRow, 3).data(Qt.UserRole).toPyObject()) t = (inventoryHash,) self.ubuntuMessagingMenuClear(t) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''update inbox set read=1 WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''update inbox set read=1 WHERE msgid=?''', *t) def tableWidgetSentItemClicked(self): currentRow = self.ui.tableWidgetSent.currentRow() @@ -2934,35 +2725,23 @@ class MyForm(QtGui.QMainWindow): def tableWidgetAddressBookItemChanged(self): currentRow = self.ui.tableWidgetAddressBook.currentRow() - shared.sqlLock.acquire() if currentRow >= 0: addressAtCurrentRow = self.ui.tableWidgetAddressBook.item( currentRow, 1).text() - t = (str(self.ui.tableWidgetAddressBook.item( - currentRow, 0).text().toUtf8()), str(addressAtCurrentRow)) - shared.sqlSubmitQueue.put( - '''UPDATE addressbook set label=? WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE addressbook set label=? WHERE address=?''', + str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()), + str(addressAtCurrentRow)) self.rerenderInboxFromLabels() self.rerenderSentToLabels() def tableWidgetSubscriptionsItemChanged(self): currentRow = self.ui.tableWidgetSubscriptions.currentRow() - shared.sqlLock.acquire() if currentRow >= 0: addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 1).text() - t = (str(self.ui.tableWidgetSubscriptions.item( - currentRow, 0).text().toUtf8()), str(addressAtCurrentRow)) - shared.sqlSubmitQueue.put( - '''UPDATE subscriptions set label=? WHERE address=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE subscriptions set label=? WHERE address=?''', + str(self.ui.tableWidgetSubscriptions.item(currentRow, 0).text().toUtf8()), + str(addressAtCurrentRow)) self.rerenderInboxFromLabels() self.rerenderSentToLabels() From 5b23d999077e474f63ca014302f2a78bcbca33c9 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Tue, 27 Aug 2013 08:13:40 -0400 Subject: [PATCH 47/86] Have helper_inbox use helper_sql --- src/helper_inbox.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/helper_inbox.py b/src/helper_inbox.py index fc420825..5d746010 100644 --- a/src/helper_inbox.py +++ b/src/helper_inbox.py @@ -1,22 +1,9 @@ -import shared +from helper_sql import * def insert(t): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?)''', *t) def trash(msgid): - t = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE inbox SET folder='trash' WHERE msgid=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid) shared.UISignalQueue.put(('removeInboxRowByMsgid',msgid)) From 9e8cbd0f0ec972e8470068d04cd736c916697607 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Tue, 27 Aug 2013 08:46:57 -0400 Subject: [PATCH 48/86] class_singleCleaner uses helper_sql --- src/class_singleCleaner.py | 70 ++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index d92a37ec..e5512e7c 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -2,6 +2,7 @@ import threading import shared import time import sys +from helper_sql import * '''The singleCleaner class is a timer-driven thread that cleans data structures to free memory, resends messages when a remote node doesn't respond, and sends pong messages to keep connections alive if the network isn't busy. It cleans these data structures in memory: @@ -27,21 +28,21 @@ class singleCleaner(threading.Thread): timeWeLastClearedInventoryAndPubkeysTables = 0 while True: - shared.sqlLock.acquire() shared.UISignalQueue.put(( 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) for hash, storedValue in shared.inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue if int(time.time()) - 3600 > receivedTime: - t = (hash, objectType, streamNumber, payload, receivedTime,'') - shared.sqlSubmitQueue.put( - '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() + sqlExecute( + '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', + hash, + objectType, + streamNumber, + payload, + receivedTime, + '') del shared.inventory[hash] - shared.sqlSubmitQueue.put('commit') shared.UISignalQueue.put(('updateStatusBar', '')) - shared.sqlLock.release() shared.broadcastToSendDataQueues(( 0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes. # If we are running as a daemon then we are going to fill up the UI @@ -53,29 +54,20 @@ class singleCleaner(threading.Thread): timeWeLastClearedInventoryAndPubkeysTables = int(time.time()) # inventory (moves data from the inventory data structure to # the on-disk sql database) - shared.sqlLock.acquire() # inventory (clears pubkeys after 28 days and everything else # after 2 days and 12 hours) - t = (int(time.time()) - shared.lengthOfTimeToLeaveObjectsInInventory, int( - time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys) - shared.sqlSubmitQueue.put( - '''DELETE FROM inventory WHERE (receivedtime'pubkey') OR (receivedtime'pubkey') OR (receivedtime (shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (msgretrynumber))): print 'It has been a long time and we haven\'t heard an acknowledgement to our msg. Sending again.' - t = (int( - time.time()), msgretrynumber + 1, 'msgqueued', ackdata) - shared.sqlSubmitQueue.put( - '''UPDATE sent SET lastactiontime=?, msgretrynumber=?, status=? WHERE ackdata=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') + sqlExecute( + '''UPDATE sent SET lastactiontime=?, msgretrynumber=?, status=? WHERE ackdata=?''', + int(time.time()), + msgretrynumber + 1, + 'msgqueued', + ackdata) shared.workerQueue.put(('sendmessage', '')) shared.UISignalQueue.put(( 'updateStatusBar', 'Doing work necessary to again attempt to deliver a message...')) - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() time.sleep(300) From 0d5f2680d404125204156d8a2a2bf81faa94d62f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 27 Aug 2013 22:29:39 -0400 Subject: [PATCH 49/86] various modifications to previous commit regarding ability to select language --- src/bitmessageqt/__init__.py | 13 +++++++++---- src/bitmessageqt/settings.py | 21 ++++++--------------- src/bitmessageqt/settings.ui | 14 ++++++++++---- src/class_outgoingSynSender.py | 2 +- src/class_sqlThread.py | 8 ++++++++ src/helper_startup.py | 7 +++++++ 6 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index eab7f0e2..8b6b1ce7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2049,14 +2049,19 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "You must restart Bitmessage for the port number change to take effect.")) shared.config.set('bitmessagesettings', 'port', str( self.settingsDialogInstance.ui.lineEditTCPPort.text())) - if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and str(self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5] == 'SOCKS': + #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText()', self.settingsDialogInstance.ui.comboBoxProxyType.currentText() + #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] + if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': if shared.statusIconColor != 'red': QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( "MainWindow", "Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any).")) - if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and str(self.settingsDialogInstance.ui.comboBoxProxyType.currentText()) == 'none': + if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] != 'SOCKS': self.statusBar().showMessage('') - shared.config.set('bitmessagesettings', 'socksproxytype', str( - self.settingsDialogInstance.ui.comboBoxProxyType.currentText())) + if self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': + shared.config.set('bitmessagesettings', 'socksproxytype', str( + self.settingsDialogInstance.ui.comboBoxProxyType.currentText())) + else: + shared.config.set('bitmessagesettings', 'socksproxytype', 'none') shared.config.set('bitmessagesettings', 'socksauthentication', str( self.settingsDialogInstance.ui.checkBoxAuthentication.isChecked())) shared.config.set('bitmessagesettings', 'sockshostname', str( diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index ad597773..0d8f9e72 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sat Aug 24 09:19:58 2013 +# Created: Tue Aug 27 22:23:38 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -71,6 +71,7 @@ class Ui_settingsDialog(object): self.horizontalLayout_2 = QtGui.QHBoxLayout(self.groupBox) self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) self.languageComboBox = QtGui.QComboBox(self.groupBox) + self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0)) self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) @@ -347,10 +348,10 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system")) self.languageComboBox.setItemText(1, _translate("settingsDialog", "English", "en")) self.languageComboBox.setItemText(2, _translate("settingsDialog", "Esperanto", "eo")) - self.languageComboBox.setItemText(3, _translate("settingsDialog", "French", "fr")) - self.languageComboBox.setItemText(4, _translate("settingsDialog", "German", "de")) - self.languageComboBox.setItemText(5, _translate("settingsDialog", "Spanish", "es")) - self.languageComboBox.setItemText(6, _translate("settingsDialog", "Russian", "ru")) + self.languageComboBox.setItemText(3, _translate("settingsDialog", "Français", "fr")) + self.languageComboBox.setItemText(4, _translate("settingsDialog", "Deutsch", "de")) + self.languageComboBox.setItemText(5, _translate("settingsDialog", "Español", "es")) + self.languageComboBox.setItemText(6, _translate("settingsDialog", "Русский", "ru")) self.languageComboBox.setItemText(7, _translate("settingsDialog", "Pirate English", "en_pirate")) self.languageComboBox.setItemText(8, _translate("settingsDialog", "Other (set in keys.dat)", "other")) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) @@ -389,13 +390,3 @@ class Ui_settingsDialog(object): self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNamecoin), _translate("settingsDialog", "Namecoin integration", None)) - -if __name__ == "__main__": - import sys - app = QtGui.QApplication(sys.argv) - settingsDialog = QtGui.QDialog() - ui = Ui_settingsDialog() - ui.setupUi(settingsDialog) - settingsDialog.show() - sys.exit(app.exec_()) - diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index eec38d8d..02117149 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -113,6 +113,12 @@ + + + 100 + 0 + + System Settings @@ -130,22 +136,22 @@ - French + Français - German + Deutsch - Spanish + Español - Russian + Русский diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index 8a929c7d..c526c9b4 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -25,7 +25,7 @@ class outgoingSynSender(threading.Thread): def run(self): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): time.sleep(2) - while True: + while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): while len(self.selfInitiatedConnections[self.streamNumber]) >= 8: # maximum number of outgoing connections = 8 time.sleep(10) if shared.shutdown: diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 5a911246..47e41b42 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -204,6 +204,14 @@ class sqlThread(threading.Thread): item = '''update settings set value=? WHERE key='version';''' parameters = (2,) self.cur.execute(item, parameters) + + if not shared.config.has_option('bitmessagesettings', 'userlocale'): + shared.config.set('bitmessagesettings', 'userlocale', 'system') + if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'): + shared.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True') + + # Are you hoping to add a new option to the keys.dat file of existing + # Bitmessage users? Add it right above this line! try: testpayload = '\x00\x00' diff --git a/src/helper_startup.py b/src/helper_startup.py index 12ea1b84..05afcb9c 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -84,6 +84,13 @@ def loadConfig(): 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'dontconnect', 'true') shared.config.set('bitmessagesettings', 'userlocale', 'system') + + # Are you hoping to add a new option to the keys.dat file? You're in + # the right place for adding it to users who install the software for + # the first time. But you must also add it to the keys.dat file of + # existing users. To do that, search the class_sqlThread.py file for the + # text: "right above this line!" + ensureNamecoinOptions() if storeConfigFilesInSameDirectoryAsProgramByDefault: From 83ffab9e4ac479215243f90b1c430282ecbf7b42 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 27 Aug 2013 22:38:32 -0400 Subject: [PATCH 50/86] manually merge #431 --- src/bitmessageqt/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8b6b1ce7..3ad41c5e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3409,7 +3409,11 @@ def run(): app = QtGui.QApplication(sys.argv) translator = QtCore.QTranslator() - locale_countrycode = str(locale.getdefaultlocale()[0]) + try: + locale_countrycode = str(locale.getdefaultlocale()[0]) + except: + # The above is not compatible with all versions of OSX. + locale_countrycode = "en_US" # Default to english. locale_lang = locale_countrycode[0:2] user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) user_lang = user_countrycode[0:2] From e8cd025b18f2be5bb9cb60c022827a4b082e87dc Mon Sep 17 00:00:00 2001 From: akh81 Date: Wed, 28 Aug 2013 04:45:35 -0500 Subject: [PATCH 51/86] updated Russian translations --- src/translations/bitmessage_ru.qm | Bin 54915 -> 60311 bytes src/translations/bitmessage_ru.ts | 505 +++++++++++++++++------------- 2 files changed, 285 insertions(+), 220 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index 2ce3c8bf2dd66f167330589e03ab1b6dbd7f2f17..f0bd634285c369e6704195da736297715118a88a 100644 GIT binary patch delta 6458 zcmZ`+33yXw);>vYnzbn{EiGlcfGh=Cy3#$Rw52UAEfuDSvW4Wf2~Crdq|m}hSVkY9 zD9Z(8sR;fKDk58ckxi6EMV4B`LBPSO&Ww&ci29%~<3Gs%e)qPaI?mIl`R;e`x#zrR zd(U^5zpi@j6O}*bur21?x2+p54($8(?457j7)PY*K}1PJ2V=0NU@ax$*5THDBK1C^ z*+Yp+MiCj75lwlYC{IiD#0cE4B(8l3QS!fuyEcO;bv$vm60x3_*B6G7dNoH>T14vB zLZb3xr1rN?A*xtM>OC(JU4D@?)kQ>~|AoSf?hv&cqrsPdCQ5vtM#i=gJ^nomY9~tHMvpcfC(1C( z>$+z0SFf5&bnyXAbwIG~Fg^B$nJ8i%J@$Sk(S+eNJ!BQpwFxxio)tiQQ(ebmqS`go za32Qtze>$7CJ@CX(xP2{qOer@y=yDc+2gdisXx(<7CQI#OrjxLx)ipWDEC>qetIt= zcY<#8nSjVH^wZsUZxcb zv2Rg+$$5#^?NxrWDVj+C7nO0fn#g}`p(^y!L4+hl753C+qV-8Cz7+`VKGm=>D~Z17 zt4h3lfGF~)DrF@A@XxEpxEB+hY*FP_@I=>ptMX@EBbt|@+CK9s03NG4YD7(3_^0aJ zbJ%BorE1d%MB(%0b?Z{qwc8trv~R0!PIZx=CXGgi4Hu)1zIDCtY2^^Z^fZ#&OOeZX&!aX(ZL#T2Q_IzA@q-KL(o z_zi?yt+q`?s7qf}ySE33`t?vRUxEmHcw4sCgM@Ojlu2cg+t@>y!fK;tix3$a#(6j3H9jV}luhmzl;pgF;dj4hi2lnHlniYnyOPXiE_-E>a}oa%?gc>i9VIjZy0=bDz!`V+nON6osUcwlVOY^~c%6#u#A#pXYt_?k4Yt^O;DrbJ%X znKh@Vq1%uAO#b10vN=yGW=YGNo&u zp7=A7ZK-zE|2&SGsr75ujX+T~?a{uzZ8TDu%D z{=lu%Uf+Usltp`Y**v0WPHXR_KtAlM_TM+KA8=R~`Q>CH^Sin}FaHQ2MY^Hq0Vr#v zE`7Qc3|OY~RzP3MFrBvndP@9px~+dgkxfp~wf3D!)VfKxBj*9p))L)2du|hrOVa(L zUmJ+zP2CS^g8@{fdvN4gqVe?&@MSP>i3#|LJE`gM+`rp{~!HWf28#e2xp}JEe8a~f2KdZBmh9)>MtC* ziWe8_zj9LD4E^B*2xX~Z_**m4Mb`~^J(1Em z#|;$|(8jBl$?F@(4OI^i=?hAObx}Ts)gnX7!sY1yN2eLqzK!CWtTb%BTtIZ_zM)l@ ziqLxuXP2O8>e>xwzkuP#ZH7;_R1h6GYPi;Xo9Lsr4A)CG!f>{sV<(C=cDmtK#4to; zli?PRZaMjpF|iteCS5g-9fgRjPBE5lauF3zF;*lp#|{}Ed$JDIt9i$0`*9W+Dqs{g zZ6{h7FmA|;#n3r#+%RVX(F(uu)uyB9{{zN@tOjP>H6BXa0S`Vg9(E(A&%SLurto4o zU69vykMXTY0NwwU@xAr0Ak=ln_PN-vzht}=2H<%sjF(43&z3&MdsF-{I6U5X?=l{Y z_#jmKIf#EOhMmCU|l-bk-|d;K`s+ z+r*P7vJIhicNY^io(OGh!BmC|;@5Cr#vk z2Zj!QYSMB^7_0Y9BiPhR|In0Bu?_9kWGcD43L&?dCJ|D!zreKSTRfj-Htl#KgQ(zt zO&3~0wJ%hg+D0Sinz{12_J-+N#WI9^o4g)QF?HBx5)G~oOLjG4JUhbD&h{sIFET8@ z@+C8o@7=J+_n#ozxi4(@#h;0eRfg>?HW6*~gdHBR9qrN-b}9-$XY>yH=^5}r`#u!L z7f+~&R5t^a+OHIv$z&lHc>`7YXB1+rK1ck_kfZof>|?ycO}4naV&BmB)`#{NzX-L9 z-;C`g&NfAfG5O(vWhRrlcPux6GZW7xbNO5vmxHT3&dg(&*L0fCFT!_47~CPAJv1S@st#NtQ;@q=SLL|XR4v9fGg%QxN%%L@l*k* z^(2s$+~fdgo=T}O_+t2Y!DqGe6_$Fz>UOyJ(n4m!kn>QH`2EtTvSJ_|i~scBP?!)@ zHcV0`*m#T0<`KMJzQ*IO=UrX89X7$`bBOa6M+eSkj#FzwxEL;ns}%d>4c27H*X!3N ziA(anPD|zb1AY!Hj_Y!S%iCr3%aJCB6xicSFM;o zIXbXwMrpF9Tt<9wtyUYf*?V)oPO(@#Fe6$V8yDhFLLyR`4?vyI5Mq}Btd%S^xKk;4 zlfj^R zyb~KeD;NPofh@qSb>Lk3BCnxPaW?Sd@TB?D2K=E3l!`e5~fgzMqP|JX=#)n zVPctM!5t@sDudM(07SrK37({|hC>I~rM8X7Zvvi10Zul@!Y_w8nJeRxB*3ifSjbZW zl|fF#KXVFYhP&)7GdD&qI_!VfWiFLDSL26xqk8M+Jb7F>Z$0d%l>n+WLw@%xr&KMMj_ zCg?s7*@tz>u(2AI+`)V4*t7a7l=6hkak;-4EQVJ`6yJ#}6>k)%|F&Fy`DGOy1ZOY+fhH|8kB0S>am-`Ko zvnlK8VnUY9Wc)LmW`Il+9jX1bU8cpIdq*og;?k#k`HNgW!NXf5*5Yg29@Z}&-fFkF z68O>@se7z;x7#Z~#@kp^? zZ?Jg14R(*kE5u2+U93IbOuHY(YCKSB^YUK11+C!l@ePf0oDM5&`0Rp<{m4cZIGj#? z4w^-IG4kb;B=N zgL#LShXIf5=6u1^%rnO%I2Q1WU1)|58A_KLNlPGaf0{wbC5oZCX!L0>@!*VJh61Sv zq#6}@dnAV}SBjRXp2MWF`Oj&E%u7wxnK!doCytQ$BngSkM)KuGWwkd3J61G|4A@LS znMi#F72*>g_VK5oGFjvIL;10p(7hj%C0y)n)?;#!n5D8RmUie$+-DRcRXyW0+{b_o zl1z@od0kz=_>Vzne9w@eC-j|7j^-1~d&P?z1ChO1mCD16^@yxc;w$mbc=pW+6jOY7_ef4^a7w3g$oEHSj!i4vM-JDphlud)kjJ-fi0*+G-jmC9@ z#DZpN*8qte+%bcu2EYQd!a9ltLS{zLfw*=+wCLX-YG*C<8!3Vl6fe0U^9UMJiUo5{ zo@0zilcju>L;bHvl;Qx6K?{Om6I2bt;Kqo%V+U5TC?eZ%o`tFs+HsAe%r6f%HYr;V zWuIOW1s5Ie!m)@Qid+be2dh(Jfv+VN=r&HRXpu$?PRBylF*Zj{ zjlf!ghX^|{#m_+(v&M7RdMx$4)#7ww8$3^8`qo<*3&n*N$xyPN&3H*3) zjL>9Z^ubqrkx&iGpF-qnrp!PbT6r{o4 zwma&1pSWXRq(8^oz@GV!GnxsSb4E9qa~jOU zi{%L{n|3d7{f1?r2iTzNpbZ?EVB2E>EqJ8RIjmC~oxZq)POaV0OJN2<31A}#cO4pS zc+G>;3H(5uEWD@D{2(H5Mu^pPGo-pfK(h&sso zP;fk?PQPq%cp(@ah(4aD>`O+jN01^OI;}2odwp;>CQyB%TAAIS8;xctlMd00mT<;F zI~C$L6aAhTv=C9sW?+rQD)8kNS8XE>l?+p0&pXen;6nvX#3xF(z6rjltC)yGBFhSs zFC2jrvTP*5AyELDqF-`_=Ie_oeamiz)Q*bj8MC8U}3Ao}M-AZa$>5 zAuE5{%JYj+c@i>$P_Yb1BfCJFF)S;r`qS=E9 z2{zF^IBLKGb`?$D<_j@s|YsV#V6Q7c{% zbVV*AD&h?zMy4W)paS)Ric(YxiG7GRCP5{pD6|RDzCQeG`e)D1H#6^hzW1E-O@14@ z=`?Hdtxou$>Br){J0A|Y9Cx_(Q2-#g0T>IMd`aj5lz#~DXMmW6K*$(Cq-W+_2PS3# z^POou3*5aC^vW7=?evU$Be>^O!(-Y2?mqy2CkF&KfG?f`%H@B|!X5cs<+%?216}zD6QbKY;n85t>IN-CrYgpN<+1 zL)f&Xz<3+XPP;^SX#jIBB652Ya48H?%hnKXMD#g7z;Y5y(bs)wSI4kGy&br=1q=J) zfth(o?#uuZuVLk{RQSXOq?ft^qfTS(F%uv^#=4XupgINxY4*SY22Gb^fZ?0bV%Q1z zs?gC;L6cgJM|RU`g0s>4*KT0mUPgQ96<~XX8D>ip@p)ikMoEsq)&Rymt_vuNW5#7r z;mf0$2^Lgf`Vq!|ffv1K$rvBf3pQ0u+}u*Y9K>u}NQ73qFuSdrfrw~k|7qIUH4mny zjHMATXF4-!hru>XS2{^(ZN>cDNovmdig~LF1k|Z2bsiC)ZE91wbd~{r@v6_ZrT|uz zs`cN~PMoS#*(s5E+2&dVq+SWG!HNU7jm3iWf1=_6<|+1x|RdTOwQ zXTEX;2Ce1gk)9;M54_jQ0$@TM@6$vkE&Ya{u#}_;f5Hd6G6I8p_!*t|$xJru_?V1y z6o(CbLKKZMWF)_$M4`sn{H9FWacwnU;6n}5IKHTd?w?u7@83#=XXyCy9VA8A=lrQ1 z>j_Kwifud*u;wo=AtGT6-@GFUxR}J>J2Vc^p5*@?MfbI%_+FMI_qOBvrkE@!PW}Ab zz;3|xhMMh3A(ucMx2gmvc2F;QLnA#|s9v2*=Bj<6F8lf}Fga3vtg3?uKTwxD)X{yl zTG{nJuro@1{+&em->UDITT;yaqOr`S4f-9|{4SVAZ(XMeZEq%1M{C0CV`ws!nn+VF zG0(oKNgTV3;`CUvc9=}@@zZ3@w4oH7((JxS*OHT_D7gY~iP4m%|C!9=q&c4{-luqlX!=GFvDHn2xB4d{o+(U~?EtUk!rZ!do_Qp!O*8`fZv|6U z=o`|sTG)PM9(kZd*zHW__~R3y(vk`s$r3Kr_)(0u2~8&&NtzDfmdT#xb5rOjBpm$@ zp?Blwv~y1A^PuMqSwjCKx*q1KwR-w{GH;;P?)YmWV9+|UF|YjpSnQT zkg2@O{}SDc{0xd;lc-H6?S@8(I<^G}EESzz)lq)^#Zk2}fPIuW>3xzUu17RZr&w)m z7{Jqe#qhT@v6?b5eyu-!3o6AOYc`SjTEyJTUy3&}N$Mnx zc;j&e@kCUC>o&DoOWmn^Va<#V{(o52u^_9awqd#FM=ln?b7p|5M&G)7g(Ote>Ob6Tk z+j8?KB&EigFqZ!0*vsw4jWpV?2k=W#?o5aQKI$@f-mnL%w1!z_e!!}J!@Mu+fRYi0 zieQ;OLbZnKVI}0^UPHY#5t|=o_&J;6S(i=%H;-fiwqE%}`yFGxCOIe{iCY=-Q^}Y4 z#_Xu?F?Y-FnTN=Y<~Q*-vHj? zJ#HxH#(8n>^gEIB<~+@*{*D#_zga!FXRNu||A)bY5Q;@ug=9D&0x9$_k**Vz=K=TF z-xgCEf}9MrY|jO7leo#$!=0PJxhee-DwY{;ejDweIL>{hR%XRbbGL~6-=@mJJ%aFm zidGc)Yn8^MvO!sSK@!c?O(Ev6Tl1CX+mlp^|J_KH($#W+H77ibFuSz5D^J_ - + MainWindow @@ -11,7 +11,6 @@ Reply - . Ответить @@ -35,7 +34,7 @@ Сохранить сообщение как ... - + New Новый адрес @@ -90,7 +89,7 @@ Форсировать отправку - + Add new entry Добавить новую запись @@ -122,7 +121,7 @@ Acknowledgement of the message received %1 - Сообщение доставлено %1 + Сообщение доставлено в %1 @@ -157,10 +156,10 @@ Since startup on %1 - С начала работы %1 + С начала работы в %1 - + Not Connected Не соединено @@ -170,9 +169,9 @@ Показать Bitmessage - + Send - Отправка + Отправить @@ -180,23 +179,23 @@ Подписки - + Address Book Адресная книга - + Quit Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -205,19 +204,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесете в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -227,192 +226,192 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесете в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены, что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите это делать, то Вы выбрали неправильную опцию. - + Processed %1 person-to-person messages. Обработано %1 сообщений. - + Processed %1 broadcast messages. Обработано %1 рассылок. - + Processed %1 public keys. Обработано %1 открытых ключей. - + Total Connections: %1 Всего соединений: %1 - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + Error: Bitmessage addresses start with BM- Please check %1 Ошибка: Bitmessage адреса начинаются с BM- Пожалуйста, проверьте %1 - + Error: The address %1 is not typed or copied correctly. Please check it. Ошибка: адрес %1 внесен или скопирован неправильно. Пожалуйста, перепроверьте. - + Error: The address %1 contains invalid characters. Please check it. Ошибка: адрес %1 содержит запрещенные символы. Пожалуйста, перепроверьте. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса в %1 слишком новая. Либо Вам нужно обновить Bitmessage, либо Ваш собеседник дал неправильный адрес. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: некоторые данные, закодированные в адресе %1, слишком короткие. Возможно, что-то не так с программой Вашего собеседника. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: некоторые данные, закодированные в адресе %1, слишком длинные. Возможно, что-то не так с программой Вашего собеседника. - + Error: Something is wrong with the address %1. Ошибка: что-то не так с адресом %1. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладе "Ваши Адреса". - + Sending to your address Отправка на Ваш собственный адрес - + Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. Ошибка: Один из адресов, на который Вы отправляете сообщение, %1, принадлежит Вам. К сожалению, Bitmessage не может отправлять сообщения самому себе. Попробуйте запустить второго клиента на другом компьютере или на виртуальной машине. - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage проделает необходимые вычисления, чтобы отправить сообщение, но не отправит его до тех пор, пока Вы не подсоединитесь к сети. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Work is queued. Вычисления поставлены в очередь. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мышки на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Work is queued. %1 Вычисления поставлены в очередь. %1 - + New Message Новое сообщение - + From От - + Address is valid. Адрес введен правильно. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Просто переименуйте существующий адрес. - + The address you entered was invalid. Ignoring it. Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в подписку несколько раз. Просто переименуйте существующую подписку. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. @@ -422,167 +421,167 @@ It is important that you back up this file. Would you like to open the file now? Bitmessage будет использовать Ваш прокси в дальнейшем, тем не менее, мы рекомендуем перезапустить Bitmessage в ручную, чтобы закрыть уже существующие соединения. - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в список несколько раз. Просто переименуйте существующий адрес. - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + All done. Closing user interface... Программа завершена. Закрываем пользовательский интерфейс... - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете ее отредактировать. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. Удалено в корзину. Чтобы попасть в корзину, Вам нужно будет найти файл корзины на диске. - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. Опции были отключены, потому что ли они либо не подходят, либо еще не выполнены под Вашу операционную систему. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещенные символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1 (Его можно поменять в настройках). - + Bitmessage Bitmessage - + To Кому - + From От кого - + Subject Тема - + Received Получено - + Inbox Входящие - + Load from Address book Взять из адресной книги - + Message: Сообщение: - + Subject: Тема: - + Send to one or more specific people Отправить одному или нескольким указанным получателям @@ -593,184 +592,184 @@ It is important that you back up this file. Would you like to open the file now? p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + To: Кому: - + From: От: - + Broadcast to everyone who is subscribed to your address Рассылка всем, кто подписался на Ваш адрес - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. Пожалуйста, учитывайте, что рассылки шифруются лишь Вашим адресом. Любой человек, который знает Ваш адрес, сможет прочитать Вашу рассылку. - + Status Статус - + Sent Отправленные - + Label (not shown to anyone) - Название (не показывается никому) + Имя (не показывается никому) - + Address Адрес - + Stream Поток - + Your Identities Ваши Адреса - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. Здесь Вы можете подписаться на рассылки от других пользователей. Все рассылки будут появляться у Вас во Входящих. Вы будете следить за всеми адресами, указанными здесь, даже если они в черном списке. - + Add new Subscription Добавить новую подписку - + Label - Название + Имя - + Subscriptions Подписки - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. Адресная книга удобна для присвоения осмысленных имен Bitmessage адресам Ваших друзей. Вы можете добавлять новые записи с помощью кнопки "Добавить новую запись", или же правым кликом мышки на сообщении. - + Name or Label - Название + Имя - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) Использовать черный список (Разрешить все входящие сообщения, кроме указанных в черном списке) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) Использовать белый список (блокировать все входящие сообщения, кроме указанных в белом списке) - + Blacklist Черный список - + Stream # № потока - + Connections Соединений - + Total connections: 0 Всего соединений: 0 - + Since startup at asdf: С начала работы программы в asdf: - + Processed 0 person-to-person message. Обработано 0 сообщений. - + Processed 0 public key. Обработано 0 открытых ключей. - + Processed 0 broadcast. Обработано 0 рассылок. - + Network Status Статус сети - + File Файл - + Settings Настройки - + Help Помощь - + Import keys Импортировать ключи - + Manage keys Управлять ключами - + About О программе - + Regenerate deterministic addresses Сгенерировать заново все адреса - + Delete all trashed messages Стереть все сообщения из корзины @@ -780,130 +779,142 @@ p, li { white-space: pre-wrap; } Сообщение отправлено в %1 - + Chan name needed Требуется имя chan-а - + You didn't enter a chan name. Вы не ввели имя chan-a. - + Address already present Адрес уже существует - + Could not add chan because it appears to already be one of your identities. Не могу добавить chan, потому что это один из Ваших уже существующих адресов. - + Success Отлично - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. Chan был успешно создан. Чтобы добавить других в сhan, сообщите им имя chan-а и этот Bitmessage адрес: %1. Этот адрес также отображается во вкладке "Ваши Адреса". - + Address too new Адрес слишком новый - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. Этот Bitmessage адрес похож на правильный, но его версия этого адреса слишком новая. Возможно, Вам необходимо обновить программу Bitmessage. - + Address invalid Неправильный адрес - + That Bitmessage address is not valid. Этот Bitmessage адрес введен неправильно. - + Address does not match chan name Адрес не сходится с именем chan-а - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. Вы ввели верный адрес Bitmessage, но он не сходится с именем chan-а. - + Successfully joined chan. Успешно присоединились к chan-у. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + Search Поиск - + All - Всем + По всем полям - + Message Текст сообщения - + Join / Create chan Подсоединиться или создать chan Mark Unread - ... - Mark Unread + Отметить как непрочитанное - + Fetched address from namecoin identity. - + Получить адрес через Namecoin. - + Testing... - + Проверяем... - + Fetch Namecoin ID - + Получить Namecoin ID - + Ctrl+Q - + Ctrl+Q - + F1 - + F1 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> @@ -982,7 +993,7 @@ The 'Random Number' option is selected by default but deterministic ad Label (not shown to anyone except you) - Название (не показывается никому кроме Вас) + Имя (не показывается никому кроме Вас) @@ -1058,7 +1069,7 @@ The 'Random Number' option is selected by default but deterministic ad Label - Название + Имя @@ -1132,22 +1143,22 @@ The 'Random Number' option is selected by default but deterministic ad Bitmessage - Bitmessage + Bitmessage Bitmessage won't connect to anyone until you let it. - + Bitmessage не будет соединяться ни с кем, пока Вы не разрешите. Connect now - + Соединиться прямо сейчас Let me configure special network settings first - + Я хочу сперва настроить сетевые настройки @@ -1241,7 +1252,7 @@ The 'Random Number' option is selected by default but deterministic ad <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> - + <html><head/><body><p>Введите имя Вашего chan-a. Если Вы выберете достаточно сложное имя для chan-а (например, сложную и необычную секретную фразу) и никто из Ваших друзей не опубликует эту фразу, то Вам chan будет надежно зашифрован. Если Вы и кто-то другой независимо создадите chan с полностью идентичным именем, то скорее всего Вы получите в итоге один и тот же chan.</p></body></html> @@ -1305,214 +1316,268 @@ The 'Random Number' option is selected by default but deterministic ad settingsDialog - + Settings Настройки - + Start Bitmessage on user login Запускать Bitmessage при входе в систему - + Start Bitmessage in the tray (don't show main window) Запускать Bitmessage в свернутом виде (не показывать главное окно) - + Minimize to tray Сворачивать в трей - + Show notification when message received Показывать уведомления при получении новых сообщений - + Run in Portable Mode Запустить в переносном режиме - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. В переносном режиме, все сообщения и конфигурационные файлы сохраняются в той же самой папке что и сама программа. Это делает более удобным использование Bitmessage с USB-флэшки. - + User Interface Пользовательские - + Listening port Порт прослушивания - + Listen for connections on port: Прослушивать соединения на порту: - + Proxy server / Tor Прокси сервер / Tor - + Type: Тип: - + none отсутствует - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Адрес сервера: - + Port: Порт: - + Authentication Авторизация - + Username: Имя пользователя: - + Pass: Прль: - + Network Settings Сетевые настройки - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. Когда кто-либо отправляет Вам сообщение, его компьютер должен сперва решить определенную вычислительную задачу. Сложность этой задачи по умолчанию равна 1. Вы можете повысить эту сложность для новых адресов, которые Вы создадите, здесь. Таким образом, любые новые адреса, которые Вы создадите, могут требовать от отправителей сложность большую чем 1. Однако, есть одно исключение: если Вы специально добавите Вашего собеседника в адресную книгу, то Bitmessage автоматически уведомит его о том, что для него минимальная сложность будет составлять всегда всего лишь 1. - + Total difficulty: Общая сложность: - + Small message difficulty: Сложность для маленьких сообщений: - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. "Сложность для маленьких сообщений" влияет исключительно на небольшие сообщения. Увеличив это число в два раза, вы сделаете отправку маленьких сообщений в два раза сложнее, в то время как сложность отправки больших сообщений не изменится. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. "Общая сложность" влияет на абсолютное количество вычислений, которые отправитель должен провести, чтобы отправить сообщение. Увеличив это число в два раза, вы увеличите в два раза объем требуемых вычислений. - + Demanded difficulty Требуемая сложность - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. Здесь Вы можете установить максимальную вычислительную работу, которую Вы согласны проделать, чтобы отправить сообщение другому пользователю. Ноль означает, чтобы любое значение допустимо. - + Maximum acceptable total difficulty: Макс допустимая общая сложность: - + Maximum acceptable small message difficulty: Макс допустимая сложность для маленький сообщений: - + Max acceptable difficulty Макс допустимая сложность - + Listen for incoming connections when using proxy Прослушивать входящие соединения если используется прокси - + Willingly include unencrypted destination address when sending to a mobile device - + Специально прикреплять незашифрованный адрес получателя, когда посылаем на мобильное устройство - - Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - - - - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> - + <html><head/><body><p>Bitmessage умеет пользоваться программой Namecoin для того, чтобы сделать адреса более дружественными для пользователей. Например, вместо того, чтобы диктовать Вашему другу длинный и нудный адрес Bitmessage, Вы можете попросить его отправить сообщение на адрес вида <span style=" font-style:italic;">test. </span></p><p>(Перенести Ваш Bitmessage адрес в Namecoin по-прежнему пока довольно сложно).</p><p>Bitmessage может использовать либо прямо namecoind, либо уже запущенную программу nmcontrol.</p></body></html> - + Host: - + Адрес: - + Password: - + Пароль: - + Test - + Проверить - + Connect to: - + Подсоединиться к: - + Namecoind - + Namecoind - + NMControl - + NMControl - + Namecoin integration - + Интеграция с Namecoin + + + + Interface Language + Язык интерфейса + + + + System Settings + system + Язык по умолчанию + + + + English + en + English + + + + Esperanto + eo + Esperanto + + + + Français + fr + Francais + + + + Deutsch + de + Deutsch + + + + Español + es + Espanol + + + + Русский + ru + Русский + + + + Pirate English + en_pirate + Pirate English + + + + Other (set in keys.dat) + other + Другие (настроено в keys.dat) From 6b9914fe4661360314de12beeac7f61363ba7475 Mon Sep 17 00:00:00 2001 From: akh81 Date: Wed, 28 Aug 2013 04:50:52 -0500 Subject: [PATCH 52/86] updated Russian translations --- src/translations/bitmessage_ru.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 6c4819ee..3be907d0 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -1,6 +1,5 @@ - - + MainWindow @@ -1122,7 +1121,7 @@ The 'Random Number' option is selected by default but deterministic ad version ? версия ? - + Copyright © 2013 Jonathan Warren Копирайт © 2013 Джонатан Уоррен @@ -1545,7 +1544,7 @@ The 'Random Number' option is selected by default but deterministic ad - Français + Français fr Francais @@ -1557,13 +1556,13 @@ The 'Random Number' option is selected by default but deterministic ad - Español + Español es Espanol - Русский + Русский ru Русский From d879e35e26814e8d99e45d916b47abde6856df8f Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 07:27:09 -0400 Subject: [PATCH 53/86] use helper_sql for helper_sent --- src/helper_sent.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/helper_sent.py b/src/helper_sent.py index dcfe844e..75634b49 100644 --- a/src/helper_sent.py +++ b/src/helper_sent.py @@ -1,11 +1,4 @@ -import shared +from helper_sql import * def insert(t): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - + sqlExecute('''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t) From 1fb11495a603aad93a63f0a9c3fed52663341679 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 07:27:30 -0400 Subject: [PATCH 54/86] use helper_sql in class_singleWorker --- src/class_singleWorker.py | 186 +++++++++++--------------------------- 1 file changed, 51 insertions(+), 135 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index a3d0a0c5..d3c0c784 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -10,6 +10,7 @@ import sys from class_addressGenerator import pointMult import tr from debug import logger +from helper_sql import * # This thread, of which there is only one, does the heavy lifting: # calculating POWs. @@ -22,35 +23,23 @@ class singleWorker(threading.Thread): threading.Thread.__init__(self) def run(self): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT toripe FROM sent WHERE ((status='awaitingpubkey' OR status='doingpubkeypow') AND folder='sent')''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() for row in queryreturn: toripe, = row shared.neededPubkeys[toripe] = 0 # Initialize the shared.ackdataForWhichImWatching data structure using data # from the sql database. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT ackdata FROM sent where (status='msgsent' OR status='doingmsgpow')''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() for row in queryreturn: ackdata, = row print 'Watching for ackdata', ackdata.encode('hex') shared.ackdataForWhichImWatching[ackdata] = 0 - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT DISTINCT toaddress FROM sent WHERE (status='doingpubkeypow' AND folder='sent')''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() for row in queryreturn: toaddress, = row self.requestPubKey(toaddress) @@ -248,26 +237,19 @@ class singleWorker(threading.Thread): if shared.safeConfigGetBoolean(myAddress, 'chan'): payload = '\x00' * 8 + payload # Attach a fake nonce on the front # just so that it is in the correct format. - t = (hash,payload,embeddedTime,'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', + hash, + payload, + embeddedTime, + 'yes') shared.config.set( myAddress, 'lastpubkeysendtime', str(int(time.time()))) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) def sendBroadcast(self): - shared.sqlLock.acquire() - t = ('broadcastqueued',) - shared.sqlSubmitQueue.put( - '''SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder='sent' ''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder='sent' ''', 'broadcastqueued') for row in queryreturn: fromaddress, subject, body, ackdata = row status, addressVersionNumber, streamNumber, ripe = decodeAddress( @@ -355,79 +337,49 @@ class singleWorker(threading.Thread): # Update the status of the message in the 'sent' table to have # a 'broadcastsent' status - shared.sqlLock.acquire() - t = (inventoryHash,'broadcastsent', int( - time.time()), ackdata) - shared.sqlSubmitQueue.put( - 'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=?') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + 'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=?', + inventoryHash, + 'broadcastsent', + int(time.time()), + ackdata) def sendMsg(self): # Check to see if there are any messages queued to be sent - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( + queryreturn = sqlQuery( '''SELECT DISTINCT toaddress FROM sent WHERE (status='msgqueued' AND folder='sent')''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() for row in queryreturn: # For each address to which we need to send a message, check to see if we have its pubkey already. toaddress, = row toripe = decodeAddress(toaddress)[3] - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT hash FROM pubkeys WHERE hash=? ''') - shared.sqlSubmitQueue.put((toripe,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT hash FROM pubkeys WHERE hash=? ''', toripe) if queryreturn != []: # If we have the needed pubkey, set the status to doingmsgpow (we'll do it further down) - t = (toaddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''', + toaddress) else: # We don't have the needed pubkey. Set the status to 'awaitingpubkey' and request it if we haven't already if toripe in shared.neededPubkeys: # We already sent a request for the pubkey - t = (toaddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''', toaddress) shared.UISignalQueue.put(('updateSentItemStatusByHash', ( toripe, tr.translateText("MainWindow",'Encryption key was requested earlier.')))) else: # We have not yet sent a request for the pubkey - t = (toaddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''', + toaddress) shared.UISignalQueue.put(('updateSentItemStatusByHash', ( toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.')))) self.requestPubKey(toaddress) - shared.sqlLock.acquire() # Get all messages that are ready to be sent, and also all messages # which we have sent in the last 28 days which were previously marked # as 'toodifficult'. If the user as raised the maximum acceptable # difficulty then those messages may now be sendable. - shared.sqlSubmitQueue.put( - '''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''') - shared.sqlSubmitQueue.put((int(time.time()) - 2419200,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''', + int(time.time()) - 2419200) for row in queryreturn: # For each message we need to send.. toaddress, toripe, fromaddress, subject, message, ackdata, status = row # There is a remote possibility that we may no longer have the @@ -436,12 +388,9 @@ class singleWorker(threading.Thread): # user sends a message but doesn't let the POW function finish, # then leaves their client off for a long time which could cause # the needed pubkey to expire and be deleted. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT hash FROM pubkeys WHERE hash=? ''') - shared.sqlSubmitQueue.put((toripe,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT hash FROM pubkeys WHERE hash=? ''', + toripe) if queryreturn == [] and toripe not in shared.neededPubkeys: # We no longer have the needed pubkey and we haven't requested # it. @@ -449,14 +398,8 @@ class singleWorker(threading.Thread): sys.stderr.write( 'For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex')) - t = (toaddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''', toaddress) shared.UISignalQueue.put(('updateSentItemStatusByHash', ( toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.')))) self.requestPubKey(toaddress) @@ -475,21 +418,15 @@ class singleWorker(threading.Thread): # mark the pubkey as 'usedpersonally' so that we don't ever delete # it. - shared.sqlLock.acquire() - t = (toripe,) - shared.sqlSubmitQueue.put( - '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') + sqlExecute( + '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''', + toripe) # Let us fetch the recipient's public key out of our database. If # the required proof of work difficulty is too hard then we'll # abort. - shared.sqlSubmitQueue.put( - 'SELECT transmitdata FROM pubkeys WHERE hash=?') - shared.sqlSubmitQueue.put((toripe,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + 'SELECT transmitdata FROM pubkeys WHERE hash=?', + toripe) if queryreturn == []: with shared.printLock: sys.stderr.write( @@ -559,14 +496,9 @@ class singleWorker(threading.Thread): if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0): # The demanded difficulty is more than we are willing # to do. - shared.sqlLock.acquire() - t = (ackdata,) - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''', + ackdata) shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float( requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) continue @@ -694,13 +626,7 @@ class singleWorker(threading.Thread): try: encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) except: - shared.sqlLock.acquire() - t = (ackdata,) - shared.sqlSubmitQueue.put('''UPDATE sent SET status='badkey' WHERE ackdata=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE sent SET status='badkey' WHERE ackdata=?''', ackdata) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))))) continue encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted @@ -741,13 +667,8 @@ class singleWorker(threading.Thread): newStatus = 'msgsentnoackexpected' else: newStatus = 'msgsent' - shared.sqlLock.acquire() - t = (inventoryHash,newStatus,ackdata,) - shared.sqlSubmitQueue.put('''UPDATE sent SET msgid=?, status=? WHERE ackdata=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE sent SET msgid=?, status=? WHERE ackdata=?''', + inventoryHash,newStatus,ackdata) def requestPubKey(self, toAddress): toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress( @@ -789,14 +710,9 @@ class singleWorker(threading.Thread): shared.broadcastToSendDataQueues(( streamNumber, 'sendinv', inventoryHash)) - t = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='doingpubkeypow' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='doingpubkeypow' ''', + toAddress) shared.UISignalQueue.put(( 'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.'))) From 92c1368691e261fd8f9ec5ecd957921a4c8f348b Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 07:53:43 -0400 Subject: [PATCH 55/86] use helper_sql in class_receiveDataThread --- src/class_receiveDataThread.py | 142 ++++++++++----------------------- 1 file changed, 42 insertions(+), 100 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index b5aa5893..d04da7a6 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -19,6 +19,7 @@ import helper_generic import helper_bitcoin import helper_inbox import helper_sent +from helper_sql import * import tr #from bitmessagemain import shared.lengthOfTimeToLeaveObjectsInInventory, shared.lengthOfTimeToHoldOnToAllPubkeys, shared.maximumAgeOfAnObjectThatIAmWillingToAccept, shared.maximumAgeOfObjectsThatIAdvertiseToOthers, shared.maximumAgeOfNodesThatIAdvertiseToOthers, shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer, shared.neededPubkeys @@ -281,16 +282,13 @@ class receiveDataThread(threading.Thread): self.sendBigInv() def sendBigInv(self): - shared.sqlLock.acquire() # Select all hashes which are younger than two days old and in this # stream. - t = (int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers, int( - time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys, self.streamNumber) - shared.sqlSubmitQueue.put( - '''SELECT hash FROM inventory WHERE ((receivedtime>? and objecttype<>'pubkey') or (receivedtime>? and objecttype='pubkey')) and streamnumber=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT hash FROM inventory WHERE ((receivedtime>? and objecttype<>'pubkey') or (receivedtime>? and objecttype='pubkey')) and streamnumber=?''', + int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers, + int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys, + self.streamNumber) bigInvList = {} for row in queryreturn: hash, = row @@ -507,15 +505,12 @@ class receiveDataThread(threading.Thread): # won't be able to send this pubkey to others (without doing # the proof of work ourselves, which this program is programmed # to not do.) - t = (ripe.digest(), '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[ - beginningOfPubkeyPosition:endOfPubkeyPosition], int(time.time()), 'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?)''', + ripe.digest(), + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition], + int(time.time()), + 'yes') # shared.workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest()))) # This will check to see whether we happen to be awaiting this # pubkey in order to send a message. If we are, it will do the @@ -657,15 +652,11 @@ class receiveDataThread(threading.Thread): # Let's store the public key in case we want to reply to this # person. - t = (ripe.digest(), '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[ - beginningOfPubkeyPosition:endOfPubkeyPosition], int(time.time()), 'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', + ripe.digest(), + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], + int(time.time()), + 'yes') # shared.workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest()))) # This will check to see whether we happen to be awaiting this # pubkey in order to send a message. If we are, it will do the POW @@ -802,14 +793,8 @@ class receiveDataThread(threading.Thread): print 'This msg IS an acknowledgement bound for me.' del shared.ackdataForWhichImWatching[encryptedData[readPosition:]] - t = ('ackreceived', encryptedData[readPosition:]) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - 'UPDATE sent SET status=? WHERE ackdata=?') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('UPDATE sent SET status=? WHERE ackdata=?', + 'ackreceived', encryptedData[readPosition:]) shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (encryptedData[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(unicode( time.strftime(shared.config.get('bitmessagesettings', 'timeformat'), time.localtime(int(time.time()))), 'utf-8'))))) return @@ -932,15 +917,12 @@ class receiveDataThread(threading.Thread): ripe.update(sha.digest()) # Let's store the public key in case we want to reply to this # person. - t = (ripe.digest(), '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[ - messageVersionLength:endOfThePublicKeyPosition], int(time.time()), 'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?)''', + ripe.digest(), + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], + int(time.time()), + 'yes') # shared.workerQueue.put(('newpubkey',(sendersAddressVersionNumber,sendersStreamNumber,ripe.digest()))) # This will check to see whether we happen to be awaiting this # pubkey in order to send a message. If we are, it will do the POW @@ -962,26 +944,18 @@ class receiveDataThread(threading.Thread): return blockMessage = False # Gets set to True if the user shouldn't see the message according to black or white lists. if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': # If we are using a blacklist - t = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT label FROM blacklist where address=? and enabled='1' ''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT label FROM blacklist where address=? and enabled='1' ''', + fromAddress) if queryreturn != []: with shared.printLock: print 'Message ignored because address is in blacklist.' blockMessage = True else: # We're using a whitelist - t = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT label FROM whitelist where address=? and enabled='1' ''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT label FROM whitelist where address=? and enabled='1' ''', + toAddress) if queryreturn == []: print 'Message ignored because address not in whitelist.' blockMessage = True @@ -1111,14 +1085,9 @@ class receiveDataThread(threading.Thread): if toRipe in shared.neededPubkeys: print 'We have been awaiting the arrival of this pubkey.' del shared.neededPubkeys[toRipe] - t = (toRipe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute( + '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', + toRipe) shared.workerQueue.put(('sendmessage', '')) else: with shared.printLock: @@ -1254,13 +1223,8 @@ class receiveDataThread(threading.Thread): print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') - t = (ripe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''', ripe) if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: print 'We HAVE used this pubkey personally. Updating time.' t = (ripe, data, embeddedTime, 'yes') @@ -1268,13 +1232,7 @@ class receiveDataThread(threading.Thread): print 'We have NOT used this pubkey personally. Inserting in database.' t = (ripe, data, embeddedTime, 'no') # This will also update the embeddedTime. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) self.possibleNewPubkey(ripe) if addressVersion == 3: @@ -1323,13 +1281,7 @@ class receiveDataThread(threading.Thread): print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') - t = (ripe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''', ripe) if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: print 'We HAVE used this pubkey personally. Updating time.' t = (ripe, data, embeddedTime, 'yes') @@ -1337,13 +1289,7 @@ class receiveDataThread(threading.Thread): print 'We have NOT used this pubkey personally. Inserting in database.' t = (ripe, data, embeddedTime, 'no') # This will also update the embeddedTime. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) self.possibleNewPubkey(ripe) @@ -1540,13 +1486,9 @@ class receiveDataThread(threading.Thread): hash] self.sendData(objectType, payload) else: - t = (hash,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select objecttype, payload from inventory where hash=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery( + '''select objecttype, payload from inventory where hash=?''', + hash) if queryreturn != []: for row in queryreturn: objectType, payload = row From 7499de4e130f73dd52ae12bfbb23b7defe81ed44 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 08:03:45 -0400 Subject: [PATCH 56/86] have shared.py use helper_sql and move the sql queues and locks to helper_sql --- src/helper_sql.py | 39 +++++++++++++++------------ src/shared.py | 68 +++++++++++++---------------------------------- 2 files changed, 40 insertions(+), 67 deletions(-) diff --git a/src/helper_sql.py b/src/helper_sql.py index f32a31d4..706fce0c 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -1,33 +1,38 @@ -import shared +import threading +import Queue + +sqlSubmitQueue = Queue.Queue() #SQLITE3 is so thread-unsafe that they won't even let you call it from different threads using your own locks. SQL objects can only be called from one thread. +sqlReturnQueue = Queue.Queue() +sqlLock = threading.Lock() def sqlQuery(sqlStatement, *args): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put(sqlStatement) + sqlLock.acquire() + sqlSubmitQueue.put(sqlStatement) if args == (): - shared.sqlSubmitQueue.put('') + sqlSubmitQueue.put('') else: - shared.sqlSubmitQueue.put(args) + sqlSubmitQueue.put(args) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlReturnQueue.get() + sqlLock.release() return queryreturn def sqlExecute(sqlStatement, *args): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put(sqlStatement) + sqlLock.acquire() + sqlSubmitQueue.put(sqlStatement) if args == (): - shared.sqlSubmitQueue.put('') + sqlSubmitQueue.put('') else: - shared.sqlSubmitQueue.put(args) + sqlSubmitQueue.put(args) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlReturnQueue.get() + sqlSubmitQueue.put('commit') + sqlLock.release() def sqlStoredProcedure(procName): - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put(procName) - shared.sqlLock.release() + sqlLock.acquire() + sqlSubmitQueue.put(procName) + sqlLock.release() diff --git a/src/shared.py b/src/shared.py index f20c49e2..0aa69558 100644 --- a/src/shared.py +++ b/src/shared.py @@ -27,7 +27,7 @@ from addresses import * import highlevelcrypto import shared import helper_startup - +from helper_sql import * config = ConfigParser.SafeConfigParser() @@ -36,9 +36,6 @@ MyECSubscriptionCryptorObjects = {} myAddressesByHash = {} #The key in this dictionary is the RIPE hash which is encoded in an address and value is the address itself. broadcastSendersForWhichImWatching = {} workerQueue = Queue.Queue() -sqlSubmitQueue = Queue.Queue() #SQLITE3 is so thread-unsafe that they won't even let you call it from different threads using your own locks. SQL objects can only be called from one thread. -sqlReturnQueue = Queue.Queue() -sqlLock = threading.Lock() UISignalQueue = Queue.Queue() addressGeneratorQueue = Queue.Queue() knownNodesLock = threading.Lock() @@ -80,12 +77,7 @@ networkDefaultPayloadLengthExtraBytes = 14000 #To make sending short messages a namecoinDefaultRpcPort = "8336" def isInSqlInventory(hash): - t = (hash,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''select hash from inventory where hash=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash) if queryreturn == []: return False else: @@ -161,41 +153,29 @@ def lookupAppdataFolder(): return dataFolder def isAddressInMyAddressBook(address): - t = (address,) - sqlLock.acquire() - sqlSubmitQueue.put('''select address from addressbook where address=?''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - sqlLock.release() + queryreturn = sqlQuery( + '''select address from addressbook where address=?''', + address) return queryreturn != [] #At this point we should really just have a isAddressInMy(book, address)... def isAddressInMySubscriptionsList(address): - t = (str(address),) # As opposed to Qt str - sqlLock.acquire() - sqlSubmitQueue.put('''select * from subscriptions where address=?''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - sqlLock.release() + queryreturn = ( + '''select * from subscriptions where address=?''', + str(address)) return queryreturn != [] def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address): if isAddressInMyAddressBook(address): return True - sqlLock.acquire() - sqlSubmitQueue.put('''SELECT address FROM whitelist where address=? and enabled = '1' ''') - sqlSubmitQueue.put((address,)) - queryreturn = sqlReturnQueue.get() - sqlLock.release() + queryreturn = sqlQuery('''SELECT address FROM whitelist where address=? and enabled = '1' ''', address) if queryreturn <> []: return True - sqlLock.acquire() - sqlSubmitQueue.put('''select address from subscriptions where address=? and enabled = '1' ''') - sqlSubmitQueue.put((address,)) - queryreturn = sqlReturnQueue.get() - sqlLock.release() + queryreturn = ( + '''select address from subscriptions where address=? and enabled = '1' ''', + address) if queryreturn <> []: return True return False @@ -259,11 +239,7 @@ def reloadBroadcastSendersForWhichImWatching(): logger.debug('reloading subscriptions...') broadcastSendersForWhichImWatching.clear() MyECSubscriptionCryptorObjects.clear() - sqlLock.acquire() - sqlSubmitQueue.put('SELECT address FROM subscriptions where enabled=1') - sqlSubmitQueue.put('') - queryreturn = sqlReturnQueue.get() - sqlLock.release() + queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1') for row in queryreturn: address, = row status,addressVersionNumber,streamNumber,hash = decodeAddress(address) @@ -297,12 +273,8 @@ def doCleanShutdown(): # This one last useless query will guarantee that the previous flush committed before we close # the program. - sqlLock.acquire() - sqlSubmitQueue.put('SELECT address FROM subscriptions') - sqlSubmitQueue.put('') - sqlReturnQueue.get() - sqlSubmitQueue.put('exit') - sqlLock.release() + sqlQuery('SELECT address FROM subscriptions') + sqlStoredProcedure('exit') logger.info('Finished flushing inventory.') # Wait long enough to guarantee that any running proof of work worker threads will check the @@ -323,16 +295,12 @@ def broadcastToSendDataQueues(data): def flushInventory(): #Note that the singleCleanerThread clears out the inventory dictionary from time to time, although it only clears things that have been in the dictionary for a long time. This clears the inventory dictionary Now. - sqlLock.acquire() for hash, storedValue in inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue - t = (hash,objectType,streamNumber,payload,receivedTime,'') - sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') - sqlSubmitQueue.put(t) - sqlReturnQueue.get() + t = () + sqlExecute('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', + hash,objectType,streamNumber,payload,receivedTime,'') del inventory[hash] - sqlSubmitQueue.put('commit') - sqlLock.release() def fixPotentiallyInvalidUTF8Data(text): try: From 03ce8ba8fbf5258365662fb3bb883d5b6710b662 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 08:47:27 -0400 Subject: [PATCH 57/86] new API method needs to use helper_sql --- src/bitmessagemain.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a34e09d5..47ee5865 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -462,13 +462,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') ackdata = self._decode(params[0], "hex") - t = (ackdata,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE ackdata=?''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() + sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', + ackdata) return 'Trashed sent message (assuming message existed).' elif method == 'sendMessage': if len(params) == 0: From 8a6d1d9cd5ef1d26c6080d8bf14f3fcbbe2ef5c3 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 10:00:27 -0400 Subject: [PATCH 58/86] Fix regression where I couldn't add a subscription --- src/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared.py b/src/shared.py index 0aa69558..265466ca 100644 --- a/src/shared.py +++ b/src/shared.py @@ -160,7 +160,7 @@ def isAddressInMyAddressBook(address): #At this point we should really just have a isAddressInMy(book, address)... def isAddressInMySubscriptionsList(address): - queryreturn = ( + queryreturn = sqlQuery( '''select * from subscriptions where address=?''', str(address)) return queryreturn != [] @@ -173,7 +173,7 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address): if queryreturn <> []: return True - queryreturn = ( + queryreturn = sqlQuery( '''select address from subscriptions where address=? and enabled = '1' ''', address) if queryreturn <> []: From 2165157c6e69c44c7c8cb5ceed62be619a38b999 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 29 Aug 2013 11:16:59 -0400 Subject: [PATCH 59/86] Fixed regression in adding to address book --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 94a6ff37..101750d7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1844,7 +1844,7 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "The address you entered was invalid. Ignoring it.")) def addEntryToAddressBook(self,address,label): - sqlQuery('''select * from addressbook where address=?''', address) + queryreturn = sqlQuery('''select * from addressbook where address=?''', address) if queryreturn == []: self.ui.tableWidgetAddressBook.setSortingEnabled(False) self.ui.tableWidgetAddressBook.insertRow(0) From 8d8e43b1fc828c67d497521f2f46a7accc387956 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Sat, 31 Aug 2013 10:40:11 -0400 Subject: [PATCH 60/86] Added SqlBulkExecute class so we can update inventory without a million commits --- src/class_singleCleaner.py | 26 ++++++++++++++------------ src/helper_sql.py | 28 ++++++++++++++++++++++++++++ src/shared.py | 12 ++++++------ 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index e5512e7c..3cc80868 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -30,18 +30,20 @@ class singleCleaner(threading.Thread): while True: shared.UISignalQueue.put(( 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) - for hash, storedValue in shared.inventory.items(): - objectType, streamNumber, payload, receivedTime = storedValue - if int(time.time()) - 3600 > receivedTime: - sqlExecute( - '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', - hash, - objectType, - streamNumber, - payload, - receivedTime, - '') - del shared.inventory[hash] + + with SqlBulkExecute() as sql: + for hash, storedValue in shared.inventory.items(): + objectType, streamNumber, payload, receivedTime = storedValue + if int(time.time()) - 3600 > receivedTime: + sql.execute( + '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', + hash, + objectType, + streamNumber, + payload, + receivedTime, + '') + del shared.inventory[hash] shared.UISignalQueue.put(('updateStatusBar', '')) shared.broadcastToSendDataQueues(( 0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes. diff --git a/src/helper_sql.py b/src/helper_sql.py index 706fce0c..0353f9ae 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -36,3 +36,31 @@ def sqlStoredProcedure(procName): sqlLock.acquire() sqlSubmitQueue.put(procName) sqlLock.release() + +class SqlBulkExecute: + def __enter__(self): + sqlLock.acquire() + return self + + def __exit__(self, type, value, traceback): + sqlSubmitQueue.put('commit') + sqlLock.release() + + def execute(self, sqlStatement, *args): + sqlSubmitQueue.put(sqlStatement) + + if args == (): + sqlSubmitQueue.put('') + else: + sqlSubmitQueue.put(args) + sqlReturnQueue.get() + + def query(self, sqlStatement, *args): + sqlSubmitQueue.put(sqlStatement) + + if args == (): + sqlSubmitQueue.put('') + else: + sqlSubmitQueue.put(args) + return sqlReturnQueue.get() + diff --git a/src/shared.py b/src/shared.py index 5061f902..0ff80978 100644 --- a/src/shared.py +++ b/src/shared.py @@ -305,12 +305,12 @@ def broadcastToSendDataQueues(data): def flushInventory(): #Note that the singleCleanerThread clears out the inventory dictionary from time to time, although it only clears things that have been in the dictionary for a long time. This clears the inventory dictionary Now. - for hash, storedValue in inventory.items(): - objectType, streamNumber, payload, receivedTime = storedValue - t = () - sqlExecute('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', - hash,objectType,streamNumber,payload,receivedTime,'') - del inventory[hash] + with SqlBulkExecute() as sql: + for hash, storedValue in inventory.items(): + objectType, streamNumber, payload, receivedTime = storedValue + sql.execute('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', + hash,objectType,streamNumber,payload,receivedTime,'') + del inventory[hash] def fixPotentiallyInvalidUTF8Data(text): try: From 9774cd2a5cebdd48ff7a82f5189a22efe71dd48f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 2 Sep 2013 16:51:32 -0400 Subject: [PATCH 61/86] fix #464 --- src/bitmessageqt/settings.py | 35 ++++++++++-------- src/bitmessageqt/settings.ui | 72 ++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 0d8f9e72..a02c4df5 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Tue Aug 27 22:23:38 2013 +# Created: Mon Sep 02 16:49:54 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -41,31 +41,34 @@ class Ui_settingsDialog(object): self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface")) self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface) self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) + self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) + self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 2) + self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) + self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 2) self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) self.checkBoxMinimizeToTray.setChecked(True) self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem, 10, 0, 1, 1) - self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) - self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) - self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) + self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 2) self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) - self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) - self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface) self.PortableModeDescription.setWordWrap(True) self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription")) - self.gridLayout_5.addWidget(self.PortableModeDescription, 5, 0, 1, 2) + self.gridLayout_5.addWidget(self.PortableModeDescription, 5, 0, 1, 3) self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface) self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile")) - self.gridLayout_5.addWidget(self.checkBoxWillinglySendToMobile, 6, 0, 1, 2) + self.gridLayout_5.addWidget(self.checkBoxWillinglySendToMobile, 6, 0, 1, 3) + self.labelSettingsNote = QtGui.QLabel(self.tabUserInterface) + self.labelSettingsNote.setText(_fromUtf8("")) + self.labelSettingsNote.setWordWrap(True) + self.labelSettingsNote.setObjectName(_fromUtf8("labelSettingsNote")) + self.gridLayout_5.addWidget(self.labelSettingsNote, 7, 0, 1, 2) self.groupBox = QtGui.QGroupBox(self.tabUserInterface) self.groupBox.setObjectName(_fromUtf8("groupBox")) self.horizontalLayout_2 = QtGui.QHBoxLayout(self.groupBox) @@ -83,7 +86,9 @@ class Ui_settingsDialog(object): self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) self.horizontalLayout_2.addWidget(self.languageComboBox) - self.gridLayout_5.addWidget(self.groupBox, 10, 1, 1, 1) + self.gridLayout_5.addWidget(self.groupBox, 7, 2, 2, 1) + spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_5.addItem(spacerItem, 8, 1, 1, 1) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) @@ -337,11 +342,11 @@ class Ui_settingsDialog(object): def retranslateUi(self, settingsDialog): settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) - self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) + self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) + self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None)) self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) - self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) self.PortableModeDescription.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None)) self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None)) self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 02117149..23755b91 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -37,6 +37,20 @@ User Interface + + + + Start Bitmessage on user login + + + + + + + Start Bitmessage in the tray (don't show main window) + + + @@ -47,27 +61,7 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Start Bitmessage on user login - - - - + Show notification when message received @@ -81,14 +75,7 @@ - - - - Start Bitmessage in the tray (don't show main window) - - - - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. @@ -98,14 +85,24 @@ - + Willingly include unencrypted destination address when sending to a mobile device - + + + + + + + true + + + + Interface Language @@ -169,6 +166,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + From ea3cf9e00e4e24644c1a50b3a265e09d4aec3908 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 2 Sep 2013 18:24:22 -0400 Subject: [PATCH 62/86] minor changes to previous commit --- src/bitmessagemain.py | 15 +++++++-------- src/bitmessageqt/__init__.py | 8 ++++---- src/helper_sql.py | 2 ++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 47ee5865..0da1fca5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -727,16 +727,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # This is not a particularly commonly used API function. Before we # use it we'll need to fill out a field in our inventory database # which is blank by default (first20bytesofencryptedmessage). - parameters = '' queryreturn = sqlQuery( '''SELECT hash, payload FROM inventory WHERE first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''') - - for row in queryreturn: - hash, payload = row - readPosition = 16 # Nonce length + time length - readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length - t = (payload[readPosition:readPosition+20],hash) - sqlExecute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', t) + with SqlBulkExecute() as sql: + for row in queryreturn: + hash, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+20],hash) + sql.execute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', t) queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''', requestedHash) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 101750d7..a60053ea 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -558,7 +558,7 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - sql = ''' + sqlStatement = ''' SELECT toaddress, fromaddress, subject, message, status, ackdata, lastactiontime FROM sent WHERE folder="sent" AND %s LIKE ? ORDER BY lastactiontime @@ -567,7 +567,7 @@ class MyForm(QtGui.QMainWindow): while self.ui.tableWidgetSent.rowCount() > 0: self.ui.tableWidgetSent.removeRow(0) - queryreturn = sqlQuery(sql, what) + queryreturn = sqlQuery(sqlStatement, what) for row in queryreturn: toAddress, fromAddress, subject, message, status, ackdata, lastactiontime = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -678,7 +678,7 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - sql = ''' + sqlStatement = ''' SELECT msgid, toaddress, fromaddress, subject, received, message, read FROM inbox WHERE folder="inbox" AND %s LIKE ? ORDER BY received @@ -689,7 +689,7 @@ class MyForm(QtGui.QMainWindow): font = QFont() font.setBold(True) - queryreturn = sqlQuery(sql, what) + queryreturn = sqlQuery(sqlStatement, what) for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, read = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) diff --git a/src/helper_sql.py b/src/helper_sql.py index 0353f9ae..9803b2c0 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -7,6 +7,7 @@ sqlLock = threading.Lock() def sqlQuery(sqlStatement, *args): sqlLock.acquire() + print 'sqlQuery args are:', args sqlSubmitQueue.put(sqlStatement) if args == (): @@ -21,6 +22,7 @@ def sqlQuery(sqlStatement, *args): def sqlExecute(sqlStatement, *args): sqlLock.acquire() + print 'sqlExecute args are:', args sqlSubmitQueue.put(sqlStatement) if args == (): From f64461feb0c899b0b85c87aa253c3f6d7b413d36 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 2 Sep 2013 23:14:43 -0400 Subject: [PATCH 63/86] fixes to new SQL refactoring --- src/bitmessagemain.py | 8 +++----- src/bitmessageqt/__init__.py | 2 +- src/helper_sql.py | 2 -- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 0da1fca5..cb44b7ad 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -441,8 +441,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # Trash if in inbox table helper_inbox.trash(msgid) # Trash if in sent table - t = (msgid,) - sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', t) + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) return 'Trashed message (assuming message existed).' elif method == 'trashInboxMessage': if len(params) == 0: @@ -454,8 +453,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) == 0: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") - t = (msgid,) - sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', t) + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) return 'Trashed sent message (assuming message existed).' elif method == 'trashSentMessageByAckData': # This API method should only be used when msgid is not available @@ -735,7 +733,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): readPosition = 16 # Nonce length + time length readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length t = (payload[readPosition:readPosition+20],hash) - sql.execute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', t) + sql.execute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', *t) queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''', requestedHash) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a60053ea..2cdb2c97 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1606,7 +1606,7 @@ class MyForm(QtGui.QMainWindow): t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( time.time()), 'broadcastqueued', 1, 1, 'sent', 2) sqlExecute( - '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''', t) + '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t) shared.workerQueue.put(('sendbroadcast', '')) diff --git a/src/helper_sql.py b/src/helper_sql.py index 9803b2c0..0353f9ae 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -7,7 +7,6 @@ sqlLock = threading.Lock() def sqlQuery(sqlStatement, *args): sqlLock.acquire() - print 'sqlQuery args are:', args sqlSubmitQueue.put(sqlStatement) if args == (): @@ -22,7 +21,6 @@ def sqlQuery(sqlStatement, *args): def sqlExecute(sqlStatement, *args): sqlLock.acquire() - print 'sqlExecute args are:', args sqlSubmitQueue.put(sqlStatement) if args == (): From e214f0bb66b6e194727d4161b49da36d8943cc0c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 3 Sep 2013 00:20:30 -0400 Subject: [PATCH 64/86] Added ability to set a message's read status using getInboxMessageID. This rather than #368 --- src/bitmessagemain.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index cb44b7ad..ae08d8ac 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -270,6 +270,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(0, 'Too many parameters!') if len(passphrase) == 0: raise APIError(1, 'The specified passphrase is blank.') + if not isinstance(eighteenByteRipe, bool): + raise APIError(23, 'Bool expected in eighteenByteRipe, saw %s instead' % type(eighteenByteRipe)) passphrase = self._decode(passphrase, "base64") if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" addressVersionNumber = 3 @@ -343,7 +345,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'getInboxMessageById' or method == 'getInboxMessageByID': if len(params) == 0: raise APIError(0, 'I need parameters!') - msgid = self._decode(params[0], "hex") + elif len(params) == 1: + msgid = self._decode(params[0], "hex") + elif len(params) >= 2: + msgid = self._decode(params[0], "hex") + readStatus = params[1] + if not isinstance(readStatus, bool): + raise APIError(23, 'Bool expected in readStatus, saw %s instead.' % type(readStatus)) + sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) data = '{"inboxMessage":[' for row in queryreturn: From 3ca4578f7ff576c7feb8678c416d58a217144164 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 3 Sep 2013 00:30:48 -0400 Subject: [PATCH 65/86] minor changes to previous commit- adding listAddressBook to API --- src/bitmessagemain.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 71ad201e..570d13c2 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -171,19 +171,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled')}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'listAddressbook': - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT label, address from addressbook''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + elif method == 'listAddressBook' or method == 'listAddressbook': + queryreturn = sqlQuery('''SELECT label, address from addressbook''') data = '{"addresses":[' for row in queryreturn: label, address = row label = shared.fixPotentiallyInvalidUTF8Data(label) if len(data) > 20: data += ',' - data += json.dumps({'label':label.encode('base64'), 'address': address}) + data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) data += ']}' return data elif method == 'createRandomAddress': From f9d2a39c3df310a3bd3062ece551c389e5f1782f Mon Sep 17 00:00:00 2001 From: Amos Bairn Date: Tue, 3 Sep 2013 12:56:07 -0700 Subject: [PATCH 66/86] Restore "import shared" to helper_inbox Commit 5b23d9 removed the line "import shared" from helper_inbox. Almost all of what shared was used for became covered by helper_sql. But, shared still needs to be imported because there is still one line that uses shared: 9: shared.UISignalQueue.put(('removeInboxRowByMsgid',msgid)) --- src/helper_inbox.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helper_inbox.py b/src/helper_inbox.py index 5d746010..20fff505 100644 --- a/src/helper_inbox.py +++ b/src/helper_inbox.py @@ -1,4 +1,5 @@ from helper_sql import * +import shared def insert(t): sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?)''', *t) From 6159d5e62264066a5724d7ddffffb13dd5d8f553 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 3 Sep 2013 18:08:29 -0400 Subject: [PATCH 67/86] Show inventory lookup rate on Network Status tab --- src/bitmessageqt/__init__.py | 10 ++++++++++ src/bitmessageqt/bitmessageui.py | 14 +++++++++----- src/bitmessageqt/bitmessageui.ui | 30 +++++++++++++++++------------- src/class_receiveDataThread.py | 8 +++++++- src/shared.py | 1 + 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2cdb2c97..034043c8 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -118,6 +118,10 @@ class MyForm(QtGui.QMainWindow): self.ui.labelSendBroadcastWarning.setVisible(False) + self.timer = QtCore.QTimer() + self.timer.start(2000) # milliseconds + QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) + # FILE MENU and other buttons QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL( "triggered()"), self.quit) @@ -1292,6 +1296,12 @@ class MyForm(QtGui.QMainWindow): elif len(shared.connectedHostsList) == 0: self.setStatusIcon('red') + # timer driven + def runEveryTwoSeconds(self): + self.ui.labelLookupsPerSecond.setText(_translate( + "MainWindow", "Inventory lookups per second: %1").arg(str(shared.numberOfInventoryLookupsPerformed/2))) + shared.numberOfInventoryLookupsPerformed = 0 + # Indicates whether or not there is a connection to the Bitmessage network connected = False diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 74505f38..f6a76ce1 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Thu Aug 15 14:19:52 2013 -# by: PyQt4 UI code generator 4.10 +# Created: Tue Sep 03 15:17:26 2013 +# by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -430,13 +430,16 @@ class Ui_MainWindow(object): self.labelBroadcastCount = QtGui.QLabel(self.networkstatus) self.labelBroadcastCount.setGeometry(QtCore.QRect(350, 150, 351, 16)) self.labelBroadcastCount.setObjectName(_fromUtf8("labelBroadcastCount")) + self.labelLookupsPerSecond = QtGui.QLabel(self.networkstatus) + self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 210, 291, 16)) + self.labelLookupsPerSecond.setObjectName(_fromUtf8("labelLookupsPerSecond")) icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.networkstatus, icon9, _fromUtf8("")) self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 23)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 18)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -554,8 +557,8 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" -"


", None)) +"\n" +"


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) @@ -621,6 +624,7 @@ class Ui_MainWindow(object): self.labelMessageCount.setText(_translate("MainWindow", "Processed 0 person-to-person message.", None)) self.labelPubkeyCount.setText(_translate("MainWindow", "Processed 0 public key.", None)) self.labelBroadcastCount.setText(_translate("MainWindow", "Processed 0 broadcast.", None)) + self.labelLookupsPerSecond.setText(_translate("MainWindow", "Inventory lookups per second: 0", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.networkstatus), _translate("MainWindow", "Network Status", None)) self.menuFile.setTitle(_translate("MainWindow", "File", None)) self.menuSettings.setTitle(_translate("MainWindow", "Settings", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 5b597d38..a802cdac 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -22,16 +22,7 @@ - - 0 - - - 0 - - - 0 - - + 0 @@ -287,8 +278,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -1040,6 +1031,19 @@ p, li { white-space: pre-wrap; } Processed 0 broadcast.
+ + + + 320 + 210 + 291 + 16 + + + + Inventory lookups per second: 0 + +
@@ -1051,7 +1055,7 @@ p, li { white-space: pre-wrap; } 0 0 795 - 23 + 18 diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index d04da7a6..9d35dfd2 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -173,6 +173,7 @@ class receiveDataThread(threading.Thread): self.payloadLength + 24:] # take this message out and then process the next message if self.data == '': while len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: + shared.numberOfInventoryLookupsPerformed += 1 random.seed() objectHash, = random.sample( self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 1) @@ -375,6 +376,7 @@ class receiveDataThread(threading.Thread): print 'The stream number encoded in this broadcast message (' + str(streamNumber) + ') does not match the stream number on which it was received. Ignoring it.' return + shared.numberOfInventoryLookupsPerformed += 1 shared.inventoryLock.acquire() self.inventoryHash = calculateInventoryHash(data) if self.inventoryHash in shared.inventory: @@ -738,6 +740,7 @@ class receiveDataThread(threading.Thread): return readPosition += streamNumberAsClaimedByMsgLength self.inventoryHash = calculateInventoryHash(data) + shared.numberOfInventoryLookupsPerformed += 1 shared.inventoryLock.acquire() if self.inventoryHash in shared.inventory: print 'We have already received this msg message. Ignoring.' @@ -1135,6 +1138,7 @@ class receiveDataThread(threading.Thread): print 'stream number embedded in this pubkey doesn\'t match our stream number. Ignoring.' return + shared.numberOfInventoryLookupsPerformed += 1 inventoryHash = calculateInventoryHash(data) shared.inventoryLock.acquire() if inventoryHash in shared.inventory: @@ -1328,6 +1332,7 @@ class receiveDataThread(threading.Thread): return readPosition += streamNumberLength + shared.numberOfInventoryLookupsPerformed += 1 inventoryHash = calculateInventoryHash(data) shared.inventoryLock.acquire() if inventoryHash in shared.inventory: @@ -1423,6 +1428,7 @@ class receiveDataThread(threading.Thread): return self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ data[lengthOfVarint:32 + lengthOfVarint]] = 0 + shared.numberOfInventoryLookupsPerformed += 1 if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory: with shared.printLock: print 'Inventory (in memory) has inventory item already.' @@ -1480,7 +1486,7 @@ class receiveDataThread(threading.Thread): with shared.printLock: print 'received getdata request for item:', hash.encode('hex') - # print 'inventory is', shared.inventory + shared.numberOfInventoryLookupsPerformed += 1 if hash in shared.inventory: objectType, streamNumber, payload, receivedTime = shared.inventory[ hash] diff --git a/src/shared.py b/src/shared.py index 0ff80978..25892b1d 100644 --- a/src/shared.py +++ b/src/shared.py @@ -66,6 +66,7 @@ clientHasReceivedIncomingConnections = False #used by API command clientStatus numberOfMessagesProcessed = 0 numberOfBroadcastsProcessed = 0 numberOfPubkeysProcessed = 0 +numberOfInventoryLookupsPerformed = 0 #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. From 5fab298559ad5852533a67f6e17287b5984731b8 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 3 Sep 2013 22:45:45 -0400 Subject: [PATCH 68/86] Refactor of the way PyBitmessage looks for interesting new objects in huge inv messages from peers --- src/class_receiveDataThread.py | 96 +++++++++++++++++++--------------- src/shared.py | 2 +- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 9d35dfd2..70fdd5b6 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -21,7 +21,8 @@ import helper_inbox import helper_sent from helper_sql import * import tr -#from bitmessagemain import shared.lengthOfTimeToLeaveObjectsInInventory, shared.lengthOfTimeToHoldOnToAllPubkeys, shared.maximumAgeOfAnObjectThatIAmWillingToAccept, shared.maximumAgeOfObjectsThatIAdvertiseToOthers, shared.maximumAgeOfNodesThatIAdvertiseToOthers, shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer, shared.neededPubkeys +from debug import logger +#from bitmessagemain import shared.lengthOfTimeToLeaveObjectsInInventory, shared.lengthOfTimeToHoldOnToAllPubkeys, shared.maximumAgeOfAnObjectThatIAmWillingToAccept, shared.maximumAgeOfObjectsThatIAdvertiseToOthers, shared.maximumAgeOfNodesThatIAdvertiseToOthers, shared.numberOfObjectsThatWeHaveYetToGetPerPeer, shared.neededPubkeys # This thread is created either by the synSenderThread(for outgoing # connections) or the singleListenerThread(for incoming connectiosn). @@ -46,7 +47,7 @@ class receiveDataThread(threading.Thread): self.peer = shared.Peer(HOST, port) self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header - self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} + self.objectsThatWeHaveYetToGetFromThisPeer = {} self.selfInitiatedConnections = selfInitiatedConnections shared.connectedHostsList[ self.peer.host] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. @@ -101,7 +102,7 @@ class receiveDataThread(threading.Thread): print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err try: - del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ + del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] except: pass @@ -172,53 +173,54 @@ class receiveDataThread(threading.Thread): self.data = self.data[ self.payloadLength + 24:] # take this message out and then process the next message if self.data == '': - while len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: + while len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: shared.numberOfInventoryLookupsPerformed += 1 random.seed() objectHash, = random.sample( - self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 1) + self.objectsThatWeHaveYetToGetFromThisPeer, 1) if objectHash in shared.inventory: with shared.printLock: print 'Inventory (in memory) already has object listed in inv message.' - del self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[ + del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] elif shared.isInSqlInventory(objectHash): if shared.verbose >= 3: with shared.printLock: print 'Inventory (SQL on disk) already has object listed in inv message.' - del self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[ + del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] else: self.sendgetdata(objectHash) - del self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[ + del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] # It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. - if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: + print 'concerning', self.peer.host, ', len(self.objectsThatWeHaveYetToGetFromThisPeer) is', len(self.objectsThatWeHaveYetToGetFromThisPeer) + if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) try: - del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ + del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass break - if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: + if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) try: - del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ + del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass - if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: + if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) - shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.peer] = len( - self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. + shared.numberOfObjectsThatWeHaveYetToGetPerPeer[self.peer] = len( + self.objectsThatWeHaveYetToGetFromThisPeer) # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. if len(self.ackDataThatWeHaveYetToSend) > 0: self.data = self.ackDataThatWeHaveYetToSend.pop() self.processData() @@ -1405,13 +1407,13 @@ class receiveDataThread(threading.Thread): # We have received an inv message def recinv(self, data): - totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = 0 # ..from all peers, counting duplicates seperately (because they take up memory) - if len(shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer) > 0: - for key, value in shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer.items(): - totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave += value + totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = 0 # this counts duplicates seperately because they take up memory + if len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) > 0: + for key, value in shared.numberOfObjectsThatWeHaveYetToGetPerPeer.items(): + totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers += value with shared.printLock: - print 'number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer:', len(shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer) - print 'totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = ', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave + print 'number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer:', len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) + print 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10]) if numberOfItemsInInv > 50000: @@ -1421,9 +1423,9 @@ class receiveDataThread(threading.Thread): print 'inv message doesn\'t contain enough data. Ignoring.' return if numberOfItemsInInv == 1: # we'll just request this data from the person who advertised the object. - if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: # inv flooding attack mitigation + if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation with shared.printLock: - print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' + print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' return self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ @@ -1432,26 +1434,38 @@ class receiveDataThread(threading.Thread): if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory: with shared.printLock: print 'Inventory (in memory) has inventory item already.' - elif shared.isInSqlInventory(data[lengthOfVarint:32 + lengthOfVarint]): print 'Inventory (SQL on disk) has inventory item already.' else: self.sendgetdata(data[lengthOfVarint:32 + lengthOfVarint]) else: - print 'inv message lists', numberOfItemsInInv, 'objects.' - for i in range(numberOfItemsInInv): # upon finishing dealing with an incoming message, the receiveDataThread will request a random object from the peer. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. - if len(data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]) == 32: # The length of an inventory hash should be 32. If it isn't 32 then the remote node is either badly programmed or behaving nefariously. - if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: # inv flooding attack mitigation - with shared.printLock: - print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave), 'from this node in particular. Ignoring the rest of this inv message.' - - break - self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[data[ - lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]] = 0 - self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[ - data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]] = 0 - shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ - self.peer] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + # There are many items listed in this inv message. Let us create a + # 'set' of objects we are aware of and a set of objects in this inv + # message so that we can diff one from the other cheaply. + startTime = time.time() + currentInventoryList = set() + queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', + self.streamNumber) + for row in queryData: + currentInventoryList.add(row[0]) + with shared.inventoryLock: + for objectHash, value in shared.inventory.items(): + currentInventoryList.add(objectHash) + advertisedSet = set() + for i in range(numberOfItemsInInv): + advertisedSet.add(data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]) + objectsNewToMe = advertisedSet - currentInventoryList + logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) + for item in objectsNewToMe: + if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation + with shared.printLock: + print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over', len(self.objectsThatWeHaveYetToGetFromThisPeer), 'from this node in particular. Ignoring the rest of this inv message.' + break + self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein + self.objectsThatWeHaveYetToGetFromThisPeer[item] = 0 # upon finishing dealing with an incoming message, the receiveDataThread will request a random object of from peer out of this data structure. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. + if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: + shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ + self.peer] = len(self.objectsThatWeHaveYetToGetFromThisPeer) # Send a getdata message to our peer to request the object with the given # hash diff --git a/src/shared.py b/src/shared.py index 25892b1d..949e14d6 100644 --- a/src/shared.py +++ b/src/shared.py @@ -53,7 +53,7 @@ alreadyAttemptedConnectionsList = { alreadyAttemptedConnectionsListLock = threading.Lock() alreadyAttemptedConnectionsListResetTime = int( time.time()) # used to clear out the alreadyAttemptedConnectionsList periodically so that we will retry connecting to hosts to which we have already tried to connect. -numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer = {} +numberOfObjectsThatWeHaveYetToGetPerPeer = {} neededPubkeys = {} eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( '>Q', random.randrange(1, 18446744073709551615)) From 36af1b528d485fbef906a6560e62ae7bb880c693 Mon Sep 17 00:00:00 2001 From: Michael Ford Date: Wed, 4 Sep 2013 19:31:50 +0800 Subject: [PATCH 69/86] Correct version number in build_osx.py --- src/build_osx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_osx.py b/src/build_osx.py index fd1bbcf9..de7dc851 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -1,7 +1,7 @@ from setuptools import setup name = "Bitmessage" -version = "0.3.4" +version = "0.3.5" mainscript = ["bitmessagemain.py"] setup( From 7ccdd14418cea6f4b0b1c149a61e5f1ebb65ec29 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 4 Sep 2013 12:53:18 -0400 Subject: [PATCH 70/86] fix #474 --- src/bitmessageqt/__init__.py | 5 ++--- src/class_receiveDataThread.py | 1 - src/namecoin.py | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 034043c8..209c7129 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2718,9 +2718,8 @@ class MyForm(QtGui.QMainWindow): inventoryHash = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - t = (inventoryHash,) - self.ubuntuMessagingMenuClear(t) - sqlExecute('''update inbox set read=1 WHERE msgid=?''', *t) + self.ubuntuMessagingMenuClear(inventoryHash) + sqlExecute('''update inbox set read=1 WHERE msgid=?''', inventoryHash) def tableWidgetSentItemClicked(self): currentRow = self.ui.tableWidgetSent.currentRow() diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 70fdd5b6..1ec15b8a 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -195,7 +195,6 @@ class receiveDataThread(threading.Thread): self.sendgetdata(objectHash) del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] # It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. - print 'concerning', self.peer.host, ', len(self.objectsThatWeHaveYetToGetFromThisPeer) is', len(self.objectsThatWeHaveYetToGetFromThisPeer) if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: with shared.printLock: print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) diff --git a/src/namecoin.py b/src/namecoin.py index f9565ccf..03cd3080 100644 --- a/src/namecoin.py +++ b/src/namecoin.py @@ -139,7 +139,7 @@ class namecoinConnection (object): assert False except Exception as exc: - print "Exception testing the namecoin connection:\n%s" % str (exc) + print "Namecoin connection test: %s" % str (exc) return ('failed', "The connection to namecoin failed.") # Helper routine that actually performs an JSON RPC call. From 1bbb8240ede85ee6c746088168e75173082a12bc Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 4 Sep 2013 16:51:19 -0400 Subject: [PATCH 71/86] modifications to API commands used by mobile device --- src/bitmessagemain.py | 49 ++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 570d13c2..a73f81e1 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -36,6 +36,7 @@ from debug import logger # Helper Functions import helper_bootstrap +import proofofwork import sys if sys.platform == 'darwin': @@ -687,14 +688,28 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'disseminatePreEncryptedMsg': # The device issuing this command to PyBitmessage supplies a msg object that has - # already been encrypted and had the necessary proof of work done for it to be - # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this msg - # object and sends it out to the rest of the Bitmessage network as if it had generated the - # message itself. Please do not yet add this to the api doc. - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - encryptedPayload, = params + # already been encrypted but which still needs the POW to be done. PyBitmessage + # accepts this msg object and sends it out to the rest of the Bitmessage network + # as if it had generated the message itself. Please do not yet add this to the + # api doc. + if len(params) != 3: + raise APIError(0, 'I need 3 parameter!') + encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, requiredPayloadLengthExtraBytes = params encryptedPayload = self._decode(encryptedPayload, "hex") + # Let us do the POW and attach it to the front + target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) + with shared.printLock: + print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes + powStartTime = time.time() + initialHash = hashlib.sha512(encryptedPayload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + with shared.printLock: + print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce + try: + print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' + except: + pass + encryptedPayload = pack('>Q', nonce) + encryptedPayload toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' @@ -705,15 +720,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.broadcastToSendDataQueues(( toStreamNumber, 'sendinv', inventoryHash)) elif method == 'disseminatePubkey': - # The device issuing this command to PyBitmessage supplies a pubkey object that has - # already had the necessary proof of work done for it to be disseminated to the rest of the - # Bitmessage network. PyBitmessage accepts this pubkey object and sends it out to the - # rest of the Bitmessage network as if it had generated the pubkey object itself. Please - # do not yet add this to the api doc. + # The device issuing this command to PyBitmessage supplies a pubkey object to be + # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this + # pubkey object and sends it out to the rest of the Bitmessage network as if it + # had generated the pubkey object itself. Please do not yet add this to the api + # doc. if len(params) != 1: raise APIError(0, 'I need 1 parameter!') payload, = params payload = self._decode(payload, "hex") + + # Let us do the POW + target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) + print '(For pubkey message via API) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce + payload = pack('>Q', nonce) + payload + pubkeyReadPosition = 8 # bypass the nonce if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time pubkeyReadPosition += 8 From 9283ce87766f1736326d0aa9c9d38ff01ae66fb2 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 4 Sep 2013 17:33:39 -0400 Subject: [PATCH 72/86] When replying using chan address, send to whole chan not just sender --- src/bitmessageqt/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 209c7129..c17ccbca 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2291,7 +2291,15 @@ class MyForm(QtGui.QMainWindow): else: self.ui.labelFrom.setText(toAddressAtCurrentInboxRow) self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) + self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) + + # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. + if shared.config.has_section(toAddressAtCurrentInboxRow): + if shared.safeConfigGetBoolean(toAddressAtCurrentInboxRow, 'chan'): + print 'original sent to a chan. Setting the to address in the reply to the chan address.' + self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) + self.ui.comboBoxSendFrom.setCurrentIndex(0) # self.ui.comboBoxSendFrom.setEditText(str(self.ui.tableWidgetInbox.item(currentInboxRow,0).text)) self.ui.textEditMessage.setText('\n\n------------------------------------------------------\n' + self.ui.tableWidgetInbox.item( From 48a3bdfefc5d85f03ac24ba7d8554f5d723df507 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Wed, 4 Sep 2013 19:25:44 -0400 Subject: [PATCH 73/86] Add chan true/false to listAddresses results --- src/bitmessagemain.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a73f81e1..ab4e80ba 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -168,8 +168,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data if len(data) > 20: data += ',' + if shared.config.has_option(addressInKeysFile, 'chan'): + chan = shared.config.getboolean(addressInKeysFile, 'chan') + else: + chan = False data += json.dumps({'label': shared.config.get(addressInKeysFile, 'label'), 'address': addressInKeysFile, 'stream': - streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled')}, indent=4, separators=(',', ': ')) + streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) data += ']}' return data elif method == 'listAddressBook' or method == 'listAddressbook': From c06bbc14f814b52eee89070eb33a5e9fd8bd7baf Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 4 Sep 2013 20:14:25 -0400 Subject: [PATCH 74/86] Give user feedback when disk is full --- src/bitmessagemain.py | 1 + src/bitmessageqt/__init__.py | 11 +++++ src/class_receiveDataThread.py | 11 ++++- src/class_sqlThread.py | 80 ++++++++++++++++++++++++++++++---- src/shared.py | 1 + 5 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a73f81e1..fb6f8012 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -859,6 +859,7 @@ if shared.useVeryEasyProofOfWorkForTesting: class Main: def start(self, daemon=False): + shared.daemon = daemon # is the application already running? If yes then exit. thisapp = singleton.singleinstance() diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index c17ccbca..35df2607 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -431,6 +431,8 @@ class MyForm(QtGui.QMainWindow): "rerenderSubscriptions()"), self.rerenderSubscriptions) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "removeInboxRowByMsgid(PyQt_PyObject)"), self.removeInboxRowByMsgid) + QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( + "displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert) self.UISignalThread.start() # Below this point, it would be good if all of the necessary global data @@ -1406,6 +1408,12 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.removeRow(i) break + def displayAlert(self, title, text, exitAfterUserClicksOk): + self.statusBar().showMessage(text) + QtGui.QMessageBox.critical(self, title, text, QMessageBox.Ok) + if exitAfterUserClicksOk: + os._exit(0) + def rerenderInboxFromLabels(self): for i in range(self.ui.tableWidgetInbox.rowCount()): addressToLookup = str(self.ui.tableWidgetInbox.item( @@ -3197,6 +3205,9 @@ class UISignaler(QThread): self.emit(SIGNAL("rerenderSubscriptions()")) elif command == 'removeInboxRowByMsgid': self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data) + elif command == 'alert': + title, text, exitAfterUserClicksOk = data + self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk) else: sys.stderr.write( 'Command sent to UISignaler not recognized: %s\n' % command) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 1ec15b8a..914d3f84 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -1772,8 +1772,15 @@ class receiveDataThread(threading.Thread): if needToWriteKnownNodesToDisk: # Runs if any nodes were new to us. Also, share those nodes with our peers. shared.knownNodesLock.acquire() output = open(shared.appdata + 'knownnodes.dat', 'wb') - pickle.dump(shared.knownNodes, output) - output.close() + try: + pickle.dump(shared.knownNodes, output) + output.close() + except Exception as err: + if "Errno 28" in str(err): + logger.fatal('(while receiveDataThread needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) shared.knownNodesLock.release() self.broadcastaddr(listOfAddressDetailsToBroadcastToPeers) with shared.printLock: diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 47e41b42..34109172 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -7,6 +7,7 @@ import sys import os from debug import logger from namecoin import ensureNamecoinOptions +import tr#anslate # 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 @@ -18,7 +19,7 @@ class sqlThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) - def run(self): + def run(self): self.conn = sqlite3.connect(shared.appdata + 'messages.dat') self.conn.text_factory = str self.cur = self.conn.cursor() @@ -230,7 +231,15 @@ class sqlThread(threading.Thread): logger.fatal('PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n') os._exit(0) except Exception as err: - logger.error(err) + if str(err) == 'database or disk is full': + logger.fatal('(While null value test) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return + else: + logger.error(err) # Let us check to see the last time we vaccumed the messages.dat file. # If it has been more than a month let's do it now. @@ -242,7 +251,16 @@ class sqlThread(threading.Thread): value, = row if int(value) < int(time.time()) - 2592000: logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...') - self.cur.execute( ''' VACUUM ''') + try: + self.cur.execute( ''' VACUUM ''') + except Exception as err: + if str(err) == 'database or disk is full': + logger.fatal('(While VACUUM) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return item = '''update settings set value=? WHERE key='lastvacuumtime';''' parameters = (int(time.time()),) self.cur.execute(item, parameters) @@ -250,7 +268,16 @@ class sqlThread(threading.Thread): while True: item = shared.sqlSubmitQueue.get() if item == 'commit': - self.conn.commit() + try: + self.conn.commit() + except Exception as err: + if str(err) == 'database or disk is full': + logger.fatal('(While committing) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return elif item == 'exit': self.conn.close() logger.info('sqlThread exiting gracefully.') @@ -259,7 +286,16 @@ class sqlThread(threading.Thread): elif item == 'movemessagstoprog': logger.debug('the sqlThread is moving the messages.dat file to the local program directory.') - self.conn.commit() + try: + self.conn.commit() + except Exception as err: + if str(err) == 'database or disk is full': + logger.fatal('(while movemessagstoprog) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return self.conn.close() shutil.move( shared.lookupAppdataFolder() + 'messages.dat', 'messages.dat') @@ -269,7 +305,16 @@ class sqlThread(threading.Thread): elif item == 'movemessagstoappdata': logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.') - self.conn.commit() + try: + self.conn.commit() + except Exception as err: + if str(err) == 'database or disk is full': + logger.fatal('(while movemessagstoappdata) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return self.conn.close() shutil.move( 'messages.dat', shared.lookupAppdataFolder() + 'messages.dat') @@ -280,7 +325,16 @@ class sqlThread(threading.Thread): self.cur.execute('''delete from inbox where folder='trash' ''') self.cur.execute('''delete from sent where folder='trash' ''') self.conn.commit() - self.cur.execute( ''' VACUUM ''') + try: + self.cur.execute( ''' VACUUM ''') + except Exception as err: + if str(err) == 'database or disk is full': + logger.fatal('(while deleteandvacuume) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return else: parameters = shared.sqlSubmitQueue.get() # print 'item', item @@ -288,8 +342,16 @@ class sqlThread(threading.Thread): try: self.cur.execute(item, parameters) except Exception as err: - logger.fatal('Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s" Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s', str(item), str(repr(parameters)), str(err)) - logger.fatal('This program shall now abruptly exit!') + if str(err) == 'database or disk is full': + logger.fatal('(while cur.execute) Alert: Your disk or data storage volume is full. sqlThread will now exit.') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + else: + return + else: + logger.fatal('Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s" Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s', str(item), str(repr(parameters)), str(err)) + logger.fatal('This program shall now abruptly exit!') os._exit(0) diff --git a/src/shared.py b/src/shared.py index 949e14d6..e7718959 100644 --- a/src/shared.py +++ b/src/shared.py @@ -67,6 +67,7 @@ numberOfMessagesProcessed = 0 numberOfBroadcastsProcessed = 0 numberOfPubkeysProcessed = 0 numberOfInventoryLookupsPerformed = 0 +daemon = False #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. From db81f0c11ed9f58cd585ed95f6ffb1b7d2b0635c Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 5 Sep 2013 06:31:52 -0400 Subject: [PATCH 75/86] Add add/deleteAddressBook APIs, extract address verification into reuable code, and make some QT stuff re-renderable --- src/bitmessagemain.py | 114 +++++++++++++++-------------------- src/bitmessageqt/__init__.py | 32 +++++++--- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a73f81e1..229b8a16 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -148,6 +148,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): except TypeError as e: raise APIError(22, "Decode error - " + str(e)) + def _verifyAddress(self, address): + status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) + if status != 'success': + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) + + if status == 'checksumfailed': + raise APIError(8, 'Checksum failed for address: ' + address) + if status == 'invalidcharacters': + raise APIError(9, 'Invalid characters in address: ' + address) + if status == 'versiontoohigh': + raise APIError(10, 'Address version number too high (or zero) in address: ' + address) + raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) + if addressVersionNumber < 2 or addressVersionNumber > 3: + raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the address.') + if streamNumber != 1: + raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the address.') + + return (status, addressVersionNumber, streamNumber, ripe) + def _handle_request(self, method, params): if method == 'helloWorld': (a, b) = params @@ -183,6 +202,33 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) data += ']}' return data + elif method == 'addAddressBook' or method == 'addAddressbook': + if len(params) != 2: + raise APIError(0, "I need label and address") + label, address = params + label = self._decode(label, "base64") + address = addBMIfNotPresent(address) + self._verifyAddress(address) + queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) + if queryreturn != []: + raise APIError(16, 'You already have this address in your address book.') + + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Added address %s to address book" % address + elif method == 'deleteAddressBook' or method == 'deleteAddressbook': + if len(params) != 1: + raise APIError(0, "I need an address") + address, = params + address = addBMIfNotPresent(address) + self._verifyAddress(address) + sqlExecute('DELETE FROM addressbook WHERE address=?', address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Deleted address book entry for %s if it existed" % address elif method == 'createRandomAddress': if len(params) == 0: raise APIError(0, 'I need parameters!') @@ -496,40 +542,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') subject = self._decode(subject, "base64") message = self._decode(message, "base64") - status, addressVersionNumber, streamNumber, toRipe = decodeAddress( - toAddress) - if status != 'success': - logger.warn('API Error 0007: Could not decode address %s. Status: %s.', toAddress, status) - - if status == 'checksumfailed': - raise APIError(8, 'Checksum failed for address: ' + toAddress) - if status == 'invalidcharacters': - raise APIError(9, 'Invalid characters in address: ' + toAddress) - if status == 'versiontoohigh': - raise APIError(10, 'Address version number too high (or zero) in address: ' + toAddress) - raise APIError(7, 'Could not decode address: ' + toAddress + ' : ' + status) - if addressVersionNumber < 2 or addressVersionNumber > 3: - raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the toAddress.') - if streamNumber != 1: - raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the toAddress.') - status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( - fromAddress) - if status != 'success': - logger.warn('API Error 0007: Could not decode address %s. Status: %s.', fromAddress, status) - - if status == 'checksumfailed': - raise APIError(8, 'Checksum failed for address: ' + fromAddress) - if status == 'invalidcharacters': - raise APIError(9, 'Invalid characters in address: ' + fromAddress) - if status == 'versiontoohigh': - raise APIError(10, 'Address version number too high (or zero) in address: ' + fromAddress) - raise APIError(7, 'Could not decode address: ' + fromAddress + ' : ' + status) - if addressVersionNumber < 2 or addressVersionNumber > 3: - raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.') - if streamNumber != 1: - raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the fromAddress.') toAddress = addBMIfNotPresent(toAddress) fromAddress = addBMIfNotPresent(fromAddress) + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(toAddress) + self._verifyAddress(fromAddress) try: fromAddressEnabled = shared.config.getboolean( fromAddress, 'enabled') @@ -570,23 +586,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): subject = self._decode(subject, "base64") message = self._decode(message, "base64") - status, addressVersionNumber, streamNumber, fromRipe = decodeAddress( - fromAddress) - if status != 'success': - logger.warn('API Error 0007: Could not decode address %s. Status: %s.', fromAddress, status) - - if status == 'checksumfailed': - raise APIError(8, 'Checksum failed for address: ' + fromAddress) - if status == 'invalidcharacters': - raise APIError(9, 'Invalid characters in address: ' + fromAddress) - if status == 'versiontoohigh': - raise APIError(10, 'Address version number too high (or zero) in address: ' + fromAddress) - raise APIError(7, 'Could not decode address: ' + fromAddress + ' : ' + status) - if addressVersionNumber < 2 or addressVersionNumber > 3: - raise APIError(11, 'the address version number currently must be 2 or 3. Others aren\'t supported. Check the fromAddress.') - if streamNumber != 1: - raise APIError(12, 'the stream number must be 1. Others aren\'t supported. Check the fromAddress.') fromAddress = addBMIfNotPresent(fromAddress) + self._verifyAddress(fromAddress) try: fromAddressEnabled = shared.config.getboolean( fromAddress, 'enabled') @@ -638,22 +639,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) > 2: raise APIError(0, 'I need either 1 or 2 parameters!') address = addBMIfNotPresent(address) - status, addressVersionNumber, streamNumber, toRipe = decodeAddress( - address) - if status != 'success': - logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) - - if status == 'checksumfailed': - raise APIError(8, 'Checksum failed for address: ' + address) - if status == 'invalidcharacters': - raise APIError(9, 'Invalid characters in address: ' + address) - if status == 'versiontoohigh': - raise APIError(10, 'Address version number too high (or zero) in address: ' + address) - raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) - if addressVersionNumber < 2 or addressVersionNumber > 3: - raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported.') - if streamNumber != 1: - raise APIError(12, 'The stream number must be 1. Others aren\'t supported.') + self._verifyAddress(address) # First we must check to see if the address is already in the # subscriptions list. queryreturn = sqlQuery('''select * from subscriptions where address=?''', address) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 209c7129..6fd3615c 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -351,16 +351,7 @@ class MyForm(QtGui.QMainWindow): self.loadSent() # Initialize the address book - queryreturn = sqlQuery('SELECT * FROM addressbook') - for row in queryreturn: - label, address = row - self.ui.tableWidgetAddressBook.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) + self.rerenderAddressBook() # Initialize the Subscriptions self.rerenderSubscriptions() @@ -427,6 +418,10 @@ class MyForm(QtGui.QMainWindow): "setStatusIcon(PyQt_PyObject)"), self.setStatusIcon) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "rerenderInboxFromLabels()"), self.rerenderInboxFromLabels) + QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( + "rerenderSentToLabels()"), self.rerenderSentToLabels) + QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( + "rerenderAddressBook()"), self.rerenderAddressBook) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "rerenderSubscriptions()"), self.rerenderSubscriptions) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( @@ -1478,6 +1473,19 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.item( i, 0).setText(unicode(toLabel, 'utf-8')) + def rerenderAddressBook(self): + self.ui.tableWidgetAddressBook.setRowCount(0) + queryreturn = sqlQuery('SELECT * FROM addressbook') + for row in queryreturn: + label, address = row + self.ui.tableWidgetAddressBook.insertRow(0) + newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) + newItem = QtGui.QTableWidgetItem(address) + newItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) + def rerenderSubscriptions(self): self.ui.tableWidgetSubscriptions.setRowCount(0) queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') @@ -3185,6 +3193,10 @@ class UISignaler(QThread): self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data) elif command == 'rerenderInboxFromLabels': self.emit(SIGNAL("rerenderInboxFromLabels()")) + elif command == 'rerenderSentToLabels': + self.emit(SIGNAL("rerenderSentToLabels()")) + elif command == 'rerenderAddressBook': + self.emit(SIGNAL("rerenderAddressBook()")) elif command == 'rerenderSubscriptions': self.emit(SIGNAL("rerenderSubscriptions()")) elif command == 'removeInboxRowByMsgid': From bfd79e0ae18bdd88533a53ee35f06ea5cefe09b0 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Thu, 5 Sep 2013 06:42:12 -0400 Subject: [PATCH 76/86] Use same argument order as addSubscription for addAddressBook --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 229b8a16..dce43f40 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -205,7 +205,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'addAddressBook' or method == 'addAddressbook': if len(params) != 2: raise APIError(0, "I need label and address") - label, address = params + address, label = params label = self._decode(label, "base64") address = addBMIfNotPresent(address) self._verifyAddress(address) From 7181da5dd6add006edea246b4342ae8efde79706 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Sep 2013 13:41:24 -0400 Subject: [PATCH 77/86] fixed #486 --- src/tr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tr.py b/src/tr.py index 1f3ef9b8..c11d1f57 100644 --- a/src/tr.py +++ b/src/tr.py @@ -1,4 +1,5 @@ import shared +import os # This is used so that the translateText function can be used when we are in daemon mode and not using any QT functions. class translateClass: From 477568f501352fc8d7463ab90659d0c5664849e0 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Sep 2013 15:06:29 -0400 Subject: [PATCH 78/86] changed API commands which modify and list the address book per discussion in #482 --- src/bitmessagemain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 66c99cfe..a5a4b320 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -195,7 +195,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'listAddressBook' or method == 'listAddressbook': + elif method == 'listAddressBookEntries' or method == 'listAddressbook': # the listAddressbook alias should be removed eventually. queryreturn = sqlQuery('''SELECT label, address from addressbook''') data = '{"addresses":[' for row in queryreturn: @@ -206,7 +206,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'addAddressBook' or method == 'addAddressbook': + elif method == 'addAddressBookEntry' or method == 'addAddressbook': # the addAddressbook alias should be deleted eventually. if len(params) != 2: raise APIError(0, "I need label and address") address, label = params @@ -222,7 +222,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.UISignalQueue.put(('rerenderSentToLabels','')) shared.UISignalQueue.put(('rerenderAddressBook','')) return "Added address %s to address book" % address - elif method == 'deleteAddressBook' or method == 'deleteAddressbook': + elif method == 'deleteAddressBookEntry' or method == 'deleteAddressbook': # The deleteAddressbook alias should be deleted eventually. if len(params) != 1: raise APIError(0, "I need an address") address, = params From a9b15f83ba8b4ce9b446eb9b3de2a4570b245031 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Sep 2013 18:55:12 -0400 Subject: [PATCH 79/86] initial testing inv refactorization --- src/bitmessagemain.py | 11 +++++++++-- src/class_receiveDataThread.py | 18 +++++++----------- src/class_sendDataThread.py | 25 +++++++++++++++++-------- src/class_singleWorker.py | 15 ++++++++++----- src/shared.py | 1 + 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a5a4b320..9cbe4471 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -46,6 +46,11 @@ if sys.platform == 'darwin': def connectToStream(streamNumber): selfInitiatedConnections[streamNumber] = {} + shared.inventorySets[streamNumber] = set() + queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber) + for row in queryData: + shared.inventorySets[streamNumber].add(row[0]) + if sys.platform[0:3] == 'win': maximumNumberOfHalfOpenConnections = 9 else: @@ -705,10 +710,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType = 'msg' shared.inventory[inventoryHash] = ( objectType, toStreamNumber, encryptedPayload, int(time.time())) + shared.inventorySets[toStreamNumber].add(inventoryHash) with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - toStreamNumber, 'sendinv', inventoryHash)) + toStreamNumber, 'advertiseobject', inventoryHash)) elif method == 'disseminatePubkey': # The device issuing this command to PyBitmessage supplies a pubkey object to be # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this @@ -741,10 +747,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType = 'pubkey' shared.inventory[inventoryHash] = ( objectType, pubkeyStreamNumber, payload, int(time.time())) + shared.inventorySets[pubkeyStreamNumber].add(inventoryHash) with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) elif method == 'getMessageDataByDestinationHash': # Method will eventually be used by a particular Android app to # select relevant messages. Do not yet add this to the api diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 914d3f84..a8b38bc4 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -392,6 +392,7 @@ class receiveDataThread(threading.Thread): objectType = 'broadcast' shared.inventory[self.inventoryHash] = ( objectType, self.streamNumber, data, embeddedTime) + shared.inventorySets[self.streamNumber].add(self.inventoryHash) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) shared.numberOfBroadcastsProcessed += 1 @@ -755,6 +756,7 @@ class receiveDataThread(threading.Thread): objectType = 'msg' shared.inventory[self.inventoryHash] = ( objectType, self.streamNumber, data, embeddedTime) + shared.inventorySets[self.streamNumber].add(self.inventoryHash) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) shared.numberOfMessagesProcessed += 1 @@ -1153,6 +1155,7 @@ class receiveDataThread(threading.Thread): objectType = 'pubkey' shared.inventory[inventoryHash] = ( objectType, self.streamNumber, data, embeddedTime) + shared.inventorySets[self.streamNumber].add(inventoryHash) shared.inventoryLock.release() self.broadcastinv(inventoryHash) shared.numberOfPubkeysProcessed += 1 @@ -1348,6 +1351,7 @@ class receiveDataThread(threading.Thread): objectType = 'getpubkey' shared.inventory[inventoryHash] = ( objectType, self.streamNumber, data, embeddedTime) + shared.inventorySets[self.streamNumber].add(inventoryHash) shared.inventoryLock.release() # This getpubkey request is valid so far. Forward to peers. self.broadcastinv(inventoryHash) @@ -1442,18 +1446,10 @@ class receiveDataThread(threading.Thread): # 'set' of objects we are aware of and a set of objects in this inv # message so that we can diff one from the other cheaply. startTime = time.time() - currentInventoryList = set() - queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', - self.streamNumber) - for row in queryData: - currentInventoryList.add(row[0]) - with shared.inventoryLock: - for objectHash, value in shared.inventory.items(): - currentInventoryList.add(objectHash) advertisedSet = set() for i in range(numberOfItemsInInv): advertisedSet.add(data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]) - objectsNewToMe = advertisedSet - currentInventoryList + objectsNewToMe = advertisedSet - shared.inventorySets[self.streamNumber] logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) for item in objectsNewToMe: if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation @@ -1552,12 +1548,12 @@ class receiveDataThread(threading.Thread): print 'sock.sendall error:', err - # Send an inv message with just one hash to all of our peers + # Advertise this object to all of our peers def broadcastinv(self, hash): with shared.printLock: print 'broadcasting inv with hash:', hash.encode('hex') - shared.broadcastToSendDataQueues((self.streamNumber, 'sendinv', hash)) + shared.broadcastToSendDataQueues((self.streamNumber, 'advertiseobject', hash)) # We have received an addr message. def recaddr(self, data): diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 867d1d70..0c8bb580 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -8,7 +8,8 @@ import random import sys import socket -#import bitmessagemain +from class_objectHashHolder import * +from addresses import * # Every connection to a peer has a sendDataThread (and also a # receiveDataThread). @@ -22,6 +23,9 @@ class sendDataThread(threading.Thread): print 'The length of sendDataQueues at sendDataThread init is:', len(shared.sendDataQueues) self.data = '' + self.objectHashHolderInstance = objectHashHolder(self.mailbox) + self.objectHashHolderInstance.start() + def setup( self, @@ -118,17 +122,20 @@ class sendDataThread(threading.Thread): shared.sendDataQueues.remove(self.mailbox) print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break + elif command == 'advertiseobject': + self.objectHashHolderInstance.holdHash(data) elif command == 'sendinv': - if data not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: - payload = '\x01' + data + payload = '' + for hash in data: + if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: + payload += hash + if payload != '': + print 'within sendinv, payload contains', len(payload)/32, 'hashes.' + payload = encodeVarint(len(payload)/32) + payload headerData = '\xe9\xbe\xb4\xd9' # magic bits, slighly different from Bitcoin's magic bits. headerData += 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' headerData += pack('>L', len(payload)) headerData += hashlib.sha512(payload).digest()[:4] - # To prevent some network analysis, 'leak' the data out - # to our peer after waiting a random amount of time - random.seed() - time.sleep(random.randrange(0, 10)) try: self.sock.sendall(headerData + payload) self.lastTimeISentData = int(time.time()) @@ -142,6 +149,8 @@ class sendDataThread(threading.Thread): shared.sendDataQueues.remove(self.mailbox) print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break + else: + print '(within sendinv) payload was empty. Not sending anything' #testing. elif command == 'pong': self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): @@ -167,4 +176,4 @@ class sendDataThread(threading.Thread): with shared.printLock: print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream - + self.objectHashHolderInstance.close() diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index d3c0c784..d8990ede 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -151,12 +151,13 @@ class singleWorker(threading.Thread): objectType = 'pubkey' shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime) + shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: print 'broadcasting inv with hash:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) shared.UISignalQueue.put(('updateStatusBar', '')) shared.config.set( myAddress, 'lastpubkeysendtime', str(int(time.time()))) @@ -224,12 +225,13 @@ class singleWorker(threading.Thread): objectType = 'pubkey' shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime) + shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: print 'broadcasting inv with hash:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) shared.UISignalQueue.put(('updateStatusBar', '')) # If this is a chan address then we won't send out the pubkey over the # network but rather will only store it in our pubkeys table so that @@ -327,10 +329,11 @@ class singleWorker(threading.Thread): objectType = 'broadcast' shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, int(time.time())) + shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(unicode( strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) @@ -650,6 +653,7 @@ class singleWorker(threading.Thread): objectType = 'msg' shared.inventory[inventoryHash] = ( objectType, toStreamNumber, encryptedPayload, int(time.time())) + shared.inventorySets[toStreamNumber].add(inventoryHash) if shared.safeConfigGetBoolean(toaddress, 'chan'): shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode( strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) @@ -659,7 +663,7 @@ class singleWorker(threading.Thread): strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) # Update the status of the message in the 'sent' table to have a # 'msgsent' status or 'msgsentnoackexpected' status. @@ -706,9 +710,10 @@ class singleWorker(threading.Thread): objectType = 'getpubkey' shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, int(time.time())) + shared.inventorySets[streamNumber].add(inventoryHash) print 'sending inv (for the getpubkey message)' shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + streamNumber, 'advertiseobject', inventoryHash)) sqlExecute( '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='doingpubkeypow' ''', diff --git a/src/shared.py b/src/shared.py index e7718959..9339987a 100644 --- a/src/shared.py +++ b/src/shared.py @@ -68,6 +68,7 @@ numberOfBroadcastsProcessed = 0 numberOfPubkeysProcessed = 0 numberOfInventoryLookupsPerformed = 0 daemon = False +inventorySets = {} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. From 2725281a6db147a627c74a98a36bb58e5803f1ea Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Sep 2013 18:58:56 -0400 Subject: [PATCH 80/86] initial testing inv refactorization --- src/class_objectHashHolder.py | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/class_objectHashHolder.py diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py new file mode 100644 index 00000000..cc636a8d --- /dev/null +++ b/src/class_objectHashHolder.py @@ -0,0 +1,39 @@ +# objectHashHolder is a timer-driven thread. One objectHashHolder thread is used +# by each sendDataThread. It uses it whenever a sendDataThread needs to +# advertise an object to peers. Instead of sending it out immediately, it must +# wait a random number of seconds for each connection so that different peers +# get different objects at different times. Thus an attacker who is +# connecting to many network nodes who receives a message first from Alice +# cannot be sure if Alice is the node who originated the message. + +import random +import time +import threading + +class objectHashHolder(threading.Thread): + def __init__(self, sendDataThreadMailbox): + threading.Thread.__init__(self) + self.shutdown = False + self.sendDataThreadMailbox = sendDataThreadMailbox # This queue is used to submit data back to our associated sendDataThread. + self.collectionOfLists = {} + for i in range(10): + self.collectionOfLists[i] = [] + + def run(self): + print 'objectHashHolder running.' + iterator = 0 + while not self.shutdown: + if len(self.collectionOfLists[iterator]) > 0: + print 'objectHashHolder is submitting', len(self.collectionOfLists[iterator]), 'items to the queue.' + self.sendDataThreadMailbox.put((0, 'sendinv', self.collectionOfLists[iterator])) + self.collectionOfLists[iterator] = [] + iterator += 1 + iterator %= 10 + time.sleep(1) + print 'objectHashHolder shutting down.' + + def holdHash(self,hash): + self.collectionOfLists[random.randrange(0, 10)].append(hash) + + def close(self): + self.shutdown = True \ No newline at end of file From 831edf0d248ea050b59565550f9df69fc65c8fe0 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Sep 2013 21:47:54 -0400 Subject: [PATCH 81/86] completed inv refactorization --- src/class_objectHashHolder.py | 3 --- src/class_sendDataThread.py | 4 ---- src/class_singleCleaner.py | 9 +++++++++ src/shared.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py index cc636a8d..9c392765 100644 --- a/src/class_objectHashHolder.py +++ b/src/class_objectHashHolder.py @@ -20,17 +20,14 @@ class objectHashHolder(threading.Thread): self.collectionOfLists[i] = [] def run(self): - print 'objectHashHolder running.' iterator = 0 while not self.shutdown: if len(self.collectionOfLists[iterator]) > 0: - print 'objectHashHolder is submitting', len(self.collectionOfLists[iterator]), 'items to the queue.' self.sendDataThreadMailbox.put((0, 'sendinv', self.collectionOfLists[iterator])) self.collectionOfLists[iterator] = [] iterator += 1 iterator %= 10 time.sleep(1) - print 'objectHashHolder shutting down.' def holdHash(self,hash): self.collectionOfLists[random.randrange(0, 10)].append(hash) diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 0c8bb580..b939b06b 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -108,7 +108,6 @@ class sendDataThread(threading.Thread): # to our peer after waiting a random amount of time # unless we have a long list of messages in our queue # to send. - random.seed() time.sleep(random.randrange(0, 10)) self.sock.sendall(data) self.lastTimeISentData = int(time.time()) @@ -130,7 +129,6 @@ class sendDataThread(threading.Thread): if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: payload += hash if payload != '': - print 'within sendinv, payload contains', len(payload)/32, 'hashes.' payload = encodeVarint(len(payload)/32) + payload headerData = '\xe9\xbe\xb4\xd9' # magic bits, slighly different from Bitcoin's magic bits. headerData += 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' @@ -149,8 +147,6 @@ class sendDataThread(threading.Thread): shared.sendDataQueues.remove(self.mailbox) print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break - else: - print '(within sendinv) payload was empty. Not sending anything' #testing. elif command == 'pong': self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 3cc80868..07d56424 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -7,6 +7,7 @@ from helper_sql import * '''The singleCleaner class is a timer-driven thread that cleans data structures to free memory, resends messages when a remote node doesn't respond, and sends pong messages to keep connections alive if the network isn't busy. It cleans these data structures in memory: inventory (moves data to the on-disk sql database) + inventorySets (clears then reloads data out of sql database) It cleans these tables on the disk: inventory (clears data more than 2 days and 12 hours old) @@ -109,4 +110,12 @@ class singleCleaner(threading.Thread): shared.workerQueue.put(('sendmessage', '')) shared.UISignalQueue.put(( 'updateStatusBar', 'Doing work necessary to again attempt to deliver a message...')) + + # Let's also clear and reload shared.inventorySets to keep it from + # taking up an unnecessary amount of memory. + for streamNumber in shared.inventorySets: + shared.inventorySets[streamNumber] = set() + queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber) + for row in queryData: + shared.inventorySets[streamNumber].add(row[0]) time.sleep(300) diff --git a/src/shared.py b/src/shared.py index 9339987a..0c0173a3 100644 --- a/src/shared.py +++ b/src/shared.py @@ -304,7 +304,7 @@ def doCleanShutdown(): def broadcastToSendDataQueues(data): # logger.debug('running broadcastToSendDataQueues') for q in sendDataQueues: - q.put((data)) + q.put(data) def flushInventory(): #Note that the singleCleanerThread clears out the inventory dictionary from time to time, although it only clears things that have been in the dictionary for a long time. This clears the inventory dictionary Now. From 804276395610d18f1ed43f0f6934c2811f28ea35 Mon Sep 17 00:00:00 2001 From: bitnukl Date: Sat, 7 Sep 2013 10:46:23 +0000 Subject: [PATCH 82/86] translation updated --- src/translations/bitmessage_de.qm | Bin 61772 -> 62134 bytes src/translations/bitmessage_de.ts | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index a81aaaba2c6772bd8f55fa65adf6efa4cf79b438..0e8e68a3353d805d725d86ff0bb32a74ed239e8f 100644 GIT binary patch delta 2593 zcmZ8ic~nz(7XDt6m%QX<5nMWoLaIj*6cH-*>-z-`CHD=U)nQ zMj09b%m(TXlATMo0TAT^)+&HK1N`w9VA%|SPXkup0$k<+>!$%>6Ck=X8JIr?qV@p5 z#vh{YS!5p%+e&+|St3BQ5NxR{uu4jHHQ;>>?5PUi$#zJ5mH>vX5*RJ%1#){asr3V4 zIELxtS^(`x%*ibSqWxg=U?)&;9ZL&t5~%uN``0;Z=SwbB5nDv=gb0^DgtQIaX}`Il(A76_P|;;t$SaLz(U zLp3p1iKixR#B3k>|JDO+$YZ4CeZcri#%!F3964n$Q{|I@92;g%@Ef4wEi*5j9Cz$u z?2O34Nq`LZaN3 z>D=Z7cqB7#k}0*L-!bnxDa-5s$qWiy0P%K#_>h5+uRSfW?4+EyN(CGD#L&Sa!PeLG z5{nGM{!Kl=rW=Ave_FTRC8*R_18S+DIhd^Halyl_4M1C&phKx8eLNSu62${W`vl(? zd;-W`2<2HU0qPPe?w%zkEQP9!R-kySP+dyEVqOVn8Ct)jfL_A6dnw?=EyDTwbfBqF z=%`Tx&vyu&gW7>DvBI*z3=*Heuue`IXbToL71G?%NZ2CL0mkEp?U8t4drv;VWeeZ< z65usn!k#4Zm#7#1a&aXI*-fMhq7=7Y5sj%@3xp?$42~CEfqV8M7tIMEJ4>{znQ~)2 zPZY(|3(N0`w){p^?R66+RkQ#ToJ2c}NGtYxL?^Quz|>E4YQ|s5?iHQc=?m086V;TC z0wViGH@YpzZ<^?4@?^m5j_84LJq0vona5VZCwf+CZcQmVXJBpK76A+G*ac0b=IRx! zT{z{=?-skbPXmnAvMW08Q_fehf$8Un;<;?7FHx*H$?D5$DDY)AA$1xt@+-S@8!^!? zVT%?}V8a@=q?gWJuVxSLA^$5Mu~nIrDqjosWad_~>)7hOECDeH*}5=764d zew8usNGg8nLx9>CiE$29hpV^5ekrAB%oh@m_7-}{M9IqfKw_+1;*&!!DKM4jW=8;b z-6g4JN-DeOlHKlOfybOA^IKEkD~Tkxj@IQ~l9H%uVAe6o(d1pExiONHSud#mx$z$d zDEvuMZ(Rm_moE8-hA1CDQ8F-@kZv__w&EWN*f-n~r3o=1Bzjn{ zbgw~Khjqna92ATLd(RGS^-Wx~6_ z@=AV+q38kU1Re}fxv;Lq|CNp;lP2jypnavd5jKW|-5z)#CB z>M3=H*2ph0@$`-P$uK-<&yZgkMF?x{<#!^<7B|c9stBpeQF-fha!{_253Hs@(_-ZV zt+a0Gt>9jrBat~Pl%LUj>Jx=Y^b26m6UCHba_kVHu*|Ch4)0Pd`cX;RNK*tIFQ!z? zQG|NjAPwDAMD?cwsfH>=VhasyIYo*~Hsxlq;z5%Ypm$XKJ$nODd{6PO@I@q|1|_aj zpi4R>Cz?&Y+n}5_^wF{FQd(&aQ#E)gm-X)>iVKuppp;z9Rc8M{`?|Nv^7XdB(oW^q zrPMhq!<8*FDaDdS!#1dJzp`DkgD7qrwl{2)ouPriWUb1&(G+N5RGt-1K*|Nxx-YH* z$A3^&HGcqZ7OJY3D(NejrMhTVMpcldsvk`NLn2h~_EUYgH<19knrk|^G700FmpsBX z?|@2L(>P*ei*+ZQk%$E7u@PFb5r{_!!l8p2-q?gVS{<5e=~O(nkPSz4p5A=&enN2vJ9CW5N=(VLElRK13TGkwT0581>Y| zc%3>#pBR&n9HOTS`ndld99o#6)>(yFsm*mUL2JCthqh^GZef>qo~wIi6fuVoAR3Q&Ux=un2E1hyqJRpB0xLp-8rBU*U_3S+pd1kp zkdL52BxoSYs(^}$x^XcQ4JblZL-2^7BC8Q0x)HYw`&Z5M@q6{E>b*X8SNOhFNVBz` z0Wb!r${>y-UI4^&z_~Xdl>vWw0n8o_$cezB?*NYpz_OvhDo2P{1_Be_g4h%YxCB9L z8%?~~gGFwTc8GvwI;1>Lz(*!70zNzo=};l?lOwdgGXU%TiReA!DUkjQ1M0iUVLyiT zs{>Z_#n|+G;G?B*xxN|5s==(R^Cb0u{IV5ZnU7g;E4;D{#KTw+5eHnpjD>T;fk}tq z6LS)nys!te$H6x#0=V`PeyjQ1_4V*S>JBUVc)O6E(-)FWExiKndocv}=7%vC4cXD9D( zRqtHe4y-+=E(+jv=Pl}@HKl-)tiBvfTu`EJOgKkL3)Ib~70kyS^*u2f$ljrTl=UWH zxGN|-Br3X9FkU@EB}NJ6&+CC4JHaWB#G>vCqpkIaSzPt(eG+M|H`kdhl5~>tt;Mz)|b~pdK^%3f{VStT85AIzrG_^Cxx^&@z9|jXChvjP^HAS3#ncg^05F=%F zSkNGT`jS#@pD)G~)&cKK6F2o{RwgHj2UAsm!&33k@cqPOv23#+PozqkXe+6H8VS6)PBIO4rbWlClFQ?4VCodfwU%iv^^zuq(>wov zNgs4rfVV@Wc`Xg}e32BCc$5-5OQC+0*z%3ECcm78tEJ5ws6gX>Dch9=t$tGOQ{F%G zt+Zzw&-HGWN|I@j-!SQ5asn|xD%~!TfFxC|A`xHfL8&e|f=s)mD+S{L{X^-7A0N?ZDV!8sTXaQv^-$C;33pG0m!ON?bKnvp$XCYi!o!&AkiU zYu6P1`v+jAs3|(uOyb)$B~H~;Xq%=yr4Nviq&fcDI%`3irlG`!^LRjW)0d2{30j*p zPKD=U?c`as=8Yp-uckV7@}AbaCWuPy)B2{dlPo7~*qGJ8Rf{&>(Zq4Pqunz9ZB9** zHu;tV@DIH0OCQP% zCFbdl*zo-_TiwYM?i|18y4vFNoR+`pF1?;M%}rf%4slc;UFW8C>@;J9u4^1$=#`@T zkFp50`^YQpE=Y{3j+1`->Cxv$LKqp2e7ec`d5`Hz}%k=gEm{)@xEUT zVQuq)h6{%9(0uOZa6?234@@uZ!PAX~&nCKa54aji-Cxiu7el4o!Giq_m3dFOKcWq% zR`dS6m4+k&z5uHEK|a=@_`NSDj8Gz zal>&l$_Z68zbzd-cw>N7Ib}L7i+)We=yp z$27Y$i4td<7J`;k>@ubPhxfyNGZieG2+V3VeV@mTSrlQa8%2w?(|T}ss;SAciFLkc z?D=ul)zlIi1Plx@J707F>ICzGLU$mp(!BKBYM}Uux#V&;a6ZReI?KebUy8ZHF`rYA zVy@{+f+3OS7dtuMH^Lc!`;q0Xx_qr_=B|ysJk=Okexa9r-7ag3eff%(#Ww!~^7gk3 diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 5f57e33a..3814fa86 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -17,7 +17,7 @@ Add sender to your Address Book - Absender zum Adressbuch hinzufügen. + Absender zum Adressbuch hinzufügen @@ -920,7 +920,7 @@ p, li { white-space: pre-wrap; } Ctrl+Q - Strg+Q + Strg+Q @@ -1487,7 +1487,7 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - + Automatische Sprachauswahl überschreiben (verwenden Sie den Landescode oder Sprachcode, z.B. "de_DE" oder "de"): From 5d64919e1f43fd9bdea1351bccac3bcb23d99c2f Mon Sep 17 00:00:00 2001 From: bitnukl Date: Sat, 7 Sep 2013 10:54:26 +0000 Subject: [PATCH 83/86] resized main window to fit translations without scrolling Now all translations fit in wothout scrolling (tested de, eo, fr and ru) --- src/bitmessageqt/bitmessageui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index f6a76ce1..eb967852 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -26,7 +26,7 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(795, 580) + MainWindow.resize(885, 580) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) From 34203d73ddeb1348ef558933e9b2be3c3f2ee9c9 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Sat, 7 Sep 2013 13:31:17 -0400 Subject: [PATCH 84/86] Only UPDATE readStatus if it changed --- src/bitmessagemain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a5a4b320..9f976c67 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -414,7 +414,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): readStatus = params[1] if not isinstance(readStatus, bool): raise APIError(23, 'Bool expected in readStatus, saw %s instead.' % type(readStatus)) - sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) + queryreturn = sqlQuery('''SELECT read FROM inbox WHERE msgid=?''', msgid) + # UPDATE is slow, only update if status is different + if queryreturn != [] and (queryreturn[0][0] == 1) != readStatus: + sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) data = '{"inboxMessage":[' for row in queryreturn: From f0bf3aad482b53b752212cabc20514529d1f70ff Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 7 Sep 2013 18:23:20 -0400 Subject: [PATCH 85/86] use locks when accessing dictionary inventory --- src/class_objectHashHolder.py | 2 +- src/class_receiveDataThread.py | 14 +++++++++----- src/class_singleCleaner.py | 32 +++++++++++++++++++------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py index 9c392765..90df7fd9 100644 --- a/src/class_objectHashHolder.py +++ b/src/class_objectHashHolder.py @@ -1,5 +1,5 @@ # objectHashHolder is a timer-driven thread. One objectHashHolder thread is used -# by each sendDataThread. It uses it whenever a sendDataThread needs to +# by each sendDataThread. The sendDataThread uses it whenever it needs to # advertise an object to peers. Instead of sending it out immediately, it must # wait a random number of seconds for each connection so that different peers # get different objects at different times. Thus an attacker who is diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index a8b38bc4..e751cef0 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -298,11 +298,12 @@ class receiveDataThread(threading.Thread): bigInvList[hash] = 0 # We also have messages in our inventory in memory (which is a python # dictionary). Let's fetch those too. - for hash, storedValue in shared.inventory.items(): - if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: - objectType, streamNumber, payload, receivedTime = storedValue - if streamNumber == self.streamNumber and receivedTime > int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers: - bigInvList[hash] = 0 + with shared.inventoryLock: + for hash, storedValue in shared.inventory.items(): + if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: + objectType, streamNumber, payload, receivedTime = storedValue + if streamNumber == self.streamNumber and receivedTime > int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers: + bigInvList[hash] = 0 numberOfObjectsInInvMessage = 0 payload = '' # Now let us start appending all of these hashes together. They will be @@ -1496,11 +1497,14 @@ class receiveDataThread(threading.Thread): print 'received getdata request for item:', hash.encode('hex') shared.numberOfInventoryLookupsPerformed += 1 + shared.inventoryLock.acquire() if hash in shared.inventory: objectType, streamNumber, payload, receivedTime = shared.inventory[ hash] + shared.inventoryLock.release() self.sendData(objectType, payload) else: + shared.inventoryLock.release() queryreturn = sqlQuery( '''select objecttype, payload from inventory where hash=?''', hash) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 07d56424..653a2461 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -32,19 +32,20 @@ class singleCleaner(threading.Thread): shared.UISignalQueue.put(( 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) - with SqlBulkExecute() as sql: - for hash, storedValue in shared.inventory.items(): - objectType, streamNumber, payload, receivedTime = storedValue - if int(time.time()) - 3600 > receivedTime: - sql.execute( - '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', - hash, - objectType, - streamNumber, - payload, - receivedTime, - '') - del shared.inventory[hash] + with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. + with SqlBulkExecute() as sql: + for hash, storedValue in shared.inventory.items(): + objectType, streamNumber, payload, receivedTime = storedValue + if int(time.time()) - 3600 > receivedTime: + sql.execute( + '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', + hash, + objectType, + streamNumber, + payload, + receivedTime, + '') + del shared.inventory[hash] shared.UISignalQueue.put(('updateStatusBar', '')) shared.broadcastToSendDataQueues(( 0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes. @@ -118,4 +119,9 @@ class singleCleaner(threading.Thread): queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber) for row in queryData: shared.inventorySets[streamNumber].add(row[0]) + with shared.inventoryLock: + for hash, storedValue in shared.inventory.items(): + objectType, streamNumber, payload, receivedTime = storedValue + if streamNumber in shared.inventorySets: + shared.inventorySets[streamNumber].add(hash) time.sleep(300) From 90e60d814552c3b26e97a3df8e309dc339210a9a Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 9 Sep 2013 19:26:32 -0400 Subject: [PATCH 86/86] delay addr messages random number of seconds --- src/class_objectHashHolder.py | 23 ++- src/class_receiveDataThread.py | 320 +++++++++++---------------------- src/class_sendDataThread.py | 29 ++- src/class_singleCleaner.py | 20 +++ src/shared.py | 1 + 5 files changed, 160 insertions(+), 233 deletions(-) diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py index 90df7fd9..c91b1c23 100644 --- a/src/class_objectHashHolder.py +++ b/src/class_objectHashHolder.py @@ -1,6 +1,7 @@ # objectHashHolder is a timer-driven thread. One objectHashHolder thread is used # by each sendDataThread. The sendDataThread uses it whenever it needs to -# advertise an object to peers. Instead of sending it out immediately, it must +# advertise an object to peers in an inv message, or advertise a peer to other +# peers in an addr message. Instead of sending them out immediately, it must # wait a random number of seconds for each connection so that different peers # get different objects at different times. Thus an attacker who is # connecting to many network nodes who receives a message first from Alice @@ -15,22 +16,30 @@ class objectHashHolder(threading.Thread): threading.Thread.__init__(self) self.shutdown = False self.sendDataThreadMailbox = sendDataThreadMailbox # This queue is used to submit data back to our associated sendDataThread. - self.collectionOfLists = {} + self.collectionOfHashLists = {} + self.collectionOfPeerLists = {} for i in range(10): - self.collectionOfLists[i] = [] + self.collectionOfHashLists[i] = [] + self.collectionOfPeerLists[i] = [] def run(self): iterator = 0 while not self.shutdown: - if len(self.collectionOfLists[iterator]) > 0: - self.sendDataThreadMailbox.put((0, 'sendinv', self.collectionOfLists[iterator])) - self.collectionOfLists[iterator] = [] + if len(self.collectionOfHashLists[iterator]) > 0: + self.sendDataThreadMailbox.put((0, 'sendinv', self.collectionOfHashLists[iterator])) + self.collectionOfHashLists[iterator] = [] + if len(self.collectionOfPeerLists[iterator]) > 0: + self.sendDataThreadMailbox.put((0, 'sendaddr', self.collectionOfPeerLists[iterator])) + self.collectionOfPeerLists[iterator] = [] iterator += 1 iterator %= 10 time.sleep(1) def holdHash(self,hash): - self.collectionOfLists[random.randrange(0, 10)].append(hash) + self.collectionOfHashLists[random.randrange(0, 10)].append(hash) + + def holdPeer(self,peerDetails): + self.collectionOfPeerLists[random.randrange(0, 10)].append(peerDetails) def close(self): self.shutdown = True \ No newline at end of file diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index e751cef0..643185fa 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -5,7 +5,6 @@ import threading import shared import hashlib import socket -import pickle import random from struct import unpack, pack import sys @@ -91,7 +90,6 @@ class receiveDataThread(threading.Thread): del self.selfInitiatedConnections[self.streamNumber][self] with shared.printLock: print 'removed self (a receiveDataThread) from selfInitiatedConnections' - except: pass shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) @@ -175,7 +173,6 @@ class receiveDataThread(threading.Thread): if self.data == '': while len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: shared.numberOfInventoryLookupsPerformed += 1 - random.seed() objectHash, = random.sample( self.objectsThatWeHaveYetToGetFromThisPeer, 1) if objectHash in shared.inventory: @@ -264,16 +261,18 @@ class receiveDataThread(threading.Thread): self.sock.settimeout( 600) # We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately. shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - remoteNodeSeenTime = shared.knownNodes[ - self.streamNumber][self.peer] with shared.printLock: print 'Connection fully established with', self.peer print 'The size of the connectedHostsList is now', len(shared.connectedHostsList) print 'The length of sendDataQueues is now:', len(shared.sendDataQueues) print 'broadcasting addr from within connectionFullyEstablished function.' - self.broadcastaddr([(int(time.time()), self.streamNumber, 1, self.peer.host, - self.peer.port)]) # This lets all of our peers know about this new node. + #self.broadcastaddr([(int(time.time()), self.streamNumber, 1, self.peer.host, + # self.remoteNodeIncomingPort)]) # This lets all of our peers know about this new node. + dataToSend = (int(time.time()), self.streamNumber, 1, self.peer.host, self.remoteNodeIncomingPort) + shared.broadcastToSendDataQueues(( + self.streamNumber, 'advertisepeer', dataToSend)) + self.sendaddr() # This is one large addr message to this one peer. if not self.initiatedConnection and len(shared.connectedHostsList) > 200: with shared.printLock: @@ -1561,7 +1560,7 @@ class receiveDataThread(threading.Thread): # We have received an addr message. def recaddr(self, data): - listOfAddressDetailsToBroadcastToPeers = [] + #listOfAddressDetailsToBroadcastToPeers = [] numberOfAddressesIncluded = 0 numberOfAddressesIncluded, lengthOfNumberOfAddresses = decodeVarint( data[:10]) @@ -1570,227 +1569,113 @@ class receiveDataThread(threading.Thread): with shared.printLock: print 'addr message contains', numberOfAddressesIncluded, 'IP addresses.' + if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0: + return + if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded): + print 'addr message does not contain the correct amount of data. Ignoring.' + return - if self.remoteProtocolVersion == 1: - if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0: - return - if len(data) != lengthOfNumberOfAddresses + (34 * numberOfAddressesIncluded): - print 'addr message does not contain the correct amount of data. Ignoring.' - return - - needToWriteKnownNodesToDisk = False - for i in range(0, numberOfAddressesIncluded): - try: - if data[16 + lengthOfNumberOfAddresses + (34 * i):28 + lengthOfNumberOfAddresses + (34 * i)] != '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': - with shared.printLock: - print 'Skipping IPv6 address.', repr(data[16 + lengthOfNumberOfAddresses + (34 * i):28 + lengthOfNumberOfAddresses + (34 * i)]) - - continue - except Exception as err: + for i in range(0, numberOfAddressesIncluded): + try: + if data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)] != '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (to test for an IPv6 address). Message: %s\n' % str(err)) + print 'Skipping IPv6 address.', repr(data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)]) - break # giving up on unpacking any more. We should still be connected however. - - try: - recaddrStream, = unpack('>I', data[4 + lengthOfNumberOfAddresses + ( - 34 * i):8 + lengthOfNumberOfAddresses + (34 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrStream). Message: %s\n' % str(err)) - - break # giving up on unpacking any more. We should still be connected however. - if recaddrStream == 0: continue - if recaddrStream != self.streamNumber and recaddrStream != (self.streamNumber * 2) and recaddrStream != ((self.streamNumber * 2) + 1): # if the embedded stream number is not in my stream or either of my child streams then ignore it. Someone might be trying funny business. - continue - try: - recaddrServices, = unpack('>Q', data[8 + lengthOfNumberOfAddresses + ( - 34 * i):16 + lengthOfNumberOfAddresses + (34 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrServices). Message: %s\n' % str(err)) + except Exception as err: + with shared.printLock: + sys.stderr.write( + 'ERROR TRYING TO UNPACK recaddr (to test for an IPv6 address). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. + break # giving up on unpacking any more. We should still be connected however. - try: - recaddrPort, = unpack('>H', data[32 + lengthOfNumberOfAddresses + ( - 34 * i):34 + lengthOfNumberOfAddresses + (34 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrPort). Message: %s\n' % str(err)) + try: + recaddrStream, = unpack('>I', data[8 + lengthOfNumberOfAddresses + ( + 38 * i):12 + lengthOfNumberOfAddresses + (38 * i)]) + except Exception as err: + with shared.printLock: + sys.stderr.write( + 'ERROR TRYING TO UNPACK recaddr (recaddrStream). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. - # print 'Within recaddr(): IP', recaddrIP, ', Port', - # recaddrPort, ', i', i - hostFromAddrMessage = socket.inet_ntoa(data[ - 28 + lengthOfNumberOfAddresses + (34 * i):32 + lengthOfNumberOfAddresses + (34 * i)]) - # print 'hostFromAddrMessage', hostFromAddrMessage - if data[28 + lengthOfNumberOfAddresses + (34 * i)] == '\x7F': - print 'Ignoring IP address in loopback range:', hostFromAddrMessage - continue - if helper_generic.isHostInPrivateIPRange(hostFromAddrMessage): - print 'Ignoring IP address in private range:', hostFromAddrMessage - continue - timeSomeoneElseReceivedMessageFromThisNode, = unpack('>I', data[lengthOfNumberOfAddresses + ( - 34 * i):4 + lengthOfNumberOfAddresses + (34 * i)]) # This is the 'time' value in the received addr message. - if recaddrStream not in shared.knownNodes: # knownNodes is a dictionary of dictionaries with one outer dictionary for each stream. If the outer stream dictionary doesn't exist yet then we must make it. - shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream] = {} - shared.knownNodesLock.release() - peerFromAddrMessage = shared.Peer(hostFromAddrMessage, recaddrPort) - if peerFromAddrMessage not in shared.knownNodes[recaddrStream]: - if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. - shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode - shared.knownNodesLock.release() - needToWriteKnownNodesToDisk = True - hostDetails = ( - timeSomeoneElseReceivedMessageFromThisNode, - recaddrStream, recaddrServices, hostFromAddrMessage, recaddrPort) - listOfAddressDetailsToBroadcastToPeers.append( - hostDetails) - else: - timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ - peerFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. - if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())): - shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode - shared.knownNodesLock.release() - if needToWriteKnownNodesToDisk: # Runs if any nodes were new to us. Also, share those nodes with our peers. + break # giving up on unpacking any more. We should still be connected however. + if recaddrStream == 0: + continue + if recaddrStream != self.streamNumber and recaddrStream != (self.streamNumber * 2) and recaddrStream != ((self.streamNumber * 2) + 1): # if the embedded stream number is not in my stream or either of my child streams then ignore it. Someone might be trying funny business. + continue + try: + recaddrServices, = unpack('>Q', data[12 + lengthOfNumberOfAddresses + ( + 38 * i):20 + lengthOfNumberOfAddresses + (38 * i)]) + except Exception as err: + with shared.printLock: + sys.stderr.write( + 'ERROR TRYING TO UNPACK recaddr (recaddrServices). Message: %s\n' % str(err)) + + break # giving up on unpacking any more. We should still be connected however. + + try: + recaddrPort, = unpack('>H', data[36 + lengthOfNumberOfAddresses + ( + 38 * i):38 + lengthOfNumberOfAddresses + (38 * i)]) + except Exception as err: + with shared.printLock: + sys.stderr.write( + 'ERROR TRYING TO UNPACK recaddr (recaddrPort). Message: %s\n' % str(err)) + + break # giving up on unpacking any more. We should still be connected however. + # print 'Within recaddr(): IP', recaddrIP, ', Port', + # recaddrPort, ', i', i + hostFromAddrMessage = socket.inet_ntoa(data[ + 32 + lengthOfNumberOfAddresses + (38 * i):36 + lengthOfNumberOfAddresses + (38 * i)]) + # print 'hostFromAddrMessage', hostFromAddrMessage + if data[32 + lengthOfNumberOfAddresses + (38 * i)] == '\x7F': + print 'Ignoring IP address in loopback range:', hostFromAddrMessage + continue + if data[32 + lengthOfNumberOfAddresses + (38 * i)] == '\x0A': + print 'Ignoring IP address in private range:', hostFromAddrMessage + continue + if data[32 + lengthOfNumberOfAddresses + (38 * i):34 + lengthOfNumberOfAddresses + (38 * i)] == '\xC0A8': + print 'Ignoring IP address in private range:', hostFromAddrMessage + continue + timeSomeoneElseReceivedMessageFromThisNode, = unpack('>Q', data[lengthOfNumberOfAddresses + ( + 38 * i):8 + lengthOfNumberOfAddresses + (38 * i)]) # This is the 'time' value in the received addr message. 64-bit. + if recaddrStream not in shared.knownNodes: # knownNodes is a dictionary of dictionaries with one outer dictionary for each stream. If the outer stream dictionary doesn't exist yet then we must make it. shared.knownNodesLock.acquire() - output = open(shared.appdata + 'knownnodes.dat', 'wb') - pickle.dump(shared.knownNodes, output) - output.close() + shared.knownNodes[recaddrStream] = {} shared.knownNodesLock.release() - self.broadcastaddr( - listOfAddressDetailsToBroadcastToPeers) # no longer broadcast - with shared.printLock: - print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.' - - elif self.remoteProtocolVersion >= 2: # The difference is that in protocol version 2, network addresses use 64 bit times rather than 32 bit times. - if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0: - return - if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded): - print 'addr message does not contain the correct amount of data. Ignoring.' - return - - needToWriteKnownNodesToDisk = False - for i in range(0, numberOfAddressesIncluded): - try: - if data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)] != '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': - with shared.printLock: - print 'Skipping IPv6 address.', repr(data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)]) - - continue - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (to test for an IPv6 address). Message: %s\n' % str(err)) - - break # giving up on unpacking any more. We should still be connected however. - - try: - recaddrStream, = unpack('>I', data[8 + lengthOfNumberOfAddresses + ( - 38 * i):12 + lengthOfNumberOfAddresses + (38 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrStream). Message: %s\n' % str(err)) - - break # giving up on unpacking any more. We should still be connected however. - if recaddrStream == 0: - continue - if recaddrStream != self.streamNumber and recaddrStream != (self.streamNumber * 2) and recaddrStream != ((self.streamNumber * 2) + 1): # if the embedded stream number is not in my stream or either of my child streams then ignore it. Someone might be trying funny business. - continue - try: - recaddrServices, = unpack('>Q', data[12 + lengthOfNumberOfAddresses + ( - 38 * i):20 + lengthOfNumberOfAddresses + (38 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrServices). Message: %s\n' % str(err)) - - break # giving up on unpacking any more. We should still be connected however. - - try: - recaddrPort, = unpack('>H', data[36 + lengthOfNumberOfAddresses + ( - 38 * i):38 + lengthOfNumberOfAddresses + (38 * i)]) - except Exception as err: - with shared.printLock: - sys.stderr.write( - 'ERROR TRYING TO UNPACK recaddr (recaddrPort). Message: %s\n' % str(err)) - - break # giving up on unpacking any more. We should still be connected however. - # print 'Within recaddr(): IP', recaddrIP, ', Port', - # recaddrPort, ', i', i - hostFromAddrMessage = socket.inet_ntoa(data[ - 32 + lengthOfNumberOfAddresses + (38 * i):36 + lengthOfNumberOfAddresses + (38 * i)]) - # print 'hostFromAddrMessage', hostFromAddrMessage - if data[32 + lengthOfNumberOfAddresses + (38 * i)] == '\x7F': - print 'Ignoring IP address in loopback range:', hostFromAddrMessage - continue - if data[32 + lengthOfNumberOfAddresses + (38 * i)] == '\x0A': - print 'Ignoring IP address in private range:', hostFromAddrMessage - continue - if data[32 + lengthOfNumberOfAddresses + (38 * i):34 + lengthOfNumberOfAddresses + (38 * i)] == '\xC0A8': - print 'Ignoring IP address in private range:', hostFromAddrMessage - continue - timeSomeoneElseReceivedMessageFromThisNode, = unpack('>Q', data[lengthOfNumberOfAddresses + ( - 38 * i):8 + lengthOfNumberOfAddresses + (38 * i)]) # This is the 'time' value in the received addr message. 64-bit. - if recaddrStream not in shared.knownNodes: # knownNodes is a dictionary of dictionaries with one outer dictionary for each stream. If the outer stream dictionary doesn't exist yet then we must make it. + peerFromAddrMessage = shared.Peer(hostFromAddrMessage, recaddrPort) + if peerFromAddrMessage not in shared.knownNodes[recaddrStream]: + if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream] = {} + shared.knownNodes[recaddrStream][peerFromAddrMessage] = ( + timeSomeoneElseReceivedMessageFromThisNode) shared.knownNodesLock.release() - peerFromAddrMessage = shared.Peer(hostFromAddrMessage, recaddrPort) - if peerFromAddrMessage not in shared.knownNodes[recaddrStream]: - if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. - shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][peerFromAddrMessage] = ( - timeSomeoneElseReceivedMessageFromThisNode) - shared.knownNodesLock.release() - with shared.printLock: - print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream + with shared.printLock: + print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream - needToWriteKnownNodesToDisk = True - hostDetails = ( - timeSomeoneElseReceivedMessageFromThisNode, - recaddrStream, recaddrServices, hostFromAddrMessage, recaddrPort) - listOfAddressDetailsToBroadcastToPeers.append( - hostDetails) - else: - timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ - peerFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. - if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())): - shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode - shared.knownNodesLock.release() - if needToWriteKnownNodesToDisk: # Runs if any nodes were new to us. Also, share those nodes with our peers. - shared.knownNodesLock.acquire() - output = open(shared.appdata + 'knownnodes.dat', 'wb') - try: - pickle.dump(shared.knownNodes, output) - output.close() - except Exception as err: - if "Errno 28" in str(err): - logger.fatal('(while receiveDataThread needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ') - shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) - if shared.daemon: - os._exit(0) - shared.knownNodesLock.release() - self.broadcastaddr(listOfAddressDetailsToBroadcastToPeers) - with shared.printLock: - print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.' + shared.needToWriteKnownNodesToDisk = True + hostDetails = ( + timeSomeoneElseReceivedMessageFromThisNode, + recaddrStream, recaddrServices, hostFromAddrMessage, recaddrPort) + #listOfAddressDetailsToBroadcastToPeers.append(hostDetails) + shared.broadcastToSendDataQueues(( + self.streamNumber, 'advertisepeer', hostDetails)) + else: + timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ + peerFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. + if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())): + shared.knownNodesLock.acquire() + shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode + shared.knownNodesLock.release() + + #if listOfAddressDetailsToBroadcastToPeers != []: + # self.broadcastaddr(listOfAddressDetailsToBroadcastToPeers) + with shared.printLock: + print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.' # Function runs when we want to broadcast an addr message to all of our # peers. Runs when we learn of nodes that we didn't previously know about # and want to share them with our peers. - def broadcastaddr(self, listOfAddressDetailsToBroadcastToPeers): + """def broadcastaddr(self, listOfAddressDetailsToBroadcastToPeers): numberOfAddressesInAddrMessage = len( listOfAddressDetailsToBroadcastToPeers) payload = '' @@ -1816,7 +1701,7 @@ class receiveDataThread(threading.Thread): print 'Broadcasting addr with', numberOfAddressesInAddrMessage, 'entries.' shared.broadcastToSendDataQueues(( - self.streamNumber, 'sendaddr', datatosend)) + self.streamNumber, 'sendaddr', datatosend))""" # Send a big addr message to our peer def sendaddr(self): @@ -1831,7 +1716,6 @@ class receiveDataThread(threading.Thread): shared.knownNodesLock.acquire() if len(shared.knownNodes[self.streamNumber]) > 0: for i in range(500): - random.seed() peer, = random.sample(shared.knownNodes[self.streamNumber], 1) if helper_generic.isHostInPrivateIPRange(peer.host): continue @@ -1839,7 +1723,6 @@ class receiveDataThread(threading.Thread): self.streamNumber][peer] if len(shared.knownNodes[self.streamNumber * 2]) > 0: for i in range(250): - random.seed() peer, = random.sample(shared.knownNodes[ self.streamNumber * 2], 1) if helper_generic.isHostInPrivateIPRange(peer.host): @@ -1848,7 +1731,6 @@ class receiveDataThread(threading.Thread): self.streamNumber * 2][peer] if len(shared.knownNodes[(self.streamNumber * 2) + 1]) > 0: for i in range(250): - random.seed() peer, = random.sample(shared.knownNodes[ (self.streamNumber * 2) + 1], 1) if helper_generic.isHostInPrivateIPRange(peer.host): @@ -1967,10 +1849,8 @@ class receiveDataThread(threading.Thread): self.peer, self.remoteProtocolVersion))) shared.knownNodesLock.acquire() - shared.knownNodes[self.streamNumber][self.peer] = int(time.time()) - output = open(shared.appdata + 'knownnodes.dat', 'wb') - pickle.dump(shared.knownNodes, output) - output.close() + shared.knownNodes[self.streamNumber][shared.Peer(self.peer.host, self.remoteNodeIncomingPort)] = int(time.time()) + shared.needToWriteKnownNodesToDisk = True shared.knownNodesLock.release() self.sendverack() diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index b939b06b..240f9c64 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -102,14 +102,31 @@ class sendDataThread(threading.Thread): print 'setting the remote node\'s protocol version in the sendData thread (ID:', id(self), ') to', specifiedRemoteProtocolVersion self.remoteProtocolVersion = specifiedRemoteProtocolVersion + elif command == 'advertisepeer': + self.objectHashHolderInstance.holdPeer(data) elif command == 'sendaddr': + numberOfAddressesInAddrMessage = len( + data) + payload = '' + for hostDetails in data: + timeLastReceivedMessageFromThisNode, streamNumber, services, host, port = hostDetails + payload += pack( + '>Q', timeLastReceivedMessageFromThisNode) # now uses 64-bit time + payload += pack('>I', streamNumber) + payload += pack( + '>q', services) # service bit flags offered by this node + payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ + socket.inet_aton(host) + payload += pack('>H', port) + + payload = encodeVarint(numberOfAddressesInAddrMessage) + payload + datatosend = '\xE9\xBE\xB4\xD9addr\x00\x00\x00\x00\x00\x00\x00\x00' + datatosend = datatosend + pack('>L', len(payload)) # payload length + datatosend = datatosend + hashlib.sha512(payload).digest()[0:4] + datatosend = datatosend + payload + try: - # To prevent some network analysis, 'leak' the data out - # to our peer after waiting a random amount of time - # unless we have a long list of messages in our queue - # to send. - time.sleep(random.randrange(0, 10)) - self.sock.sendall(data) + self.sock.sendall(datatosend) self.lastTimeISentData = int(time.time()) except: print 'sendaddr: self.sock.sendall failed' diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 653a2461..44cb893c 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -2,7 +2,11 @@ import threading import shared import time import sys +import pickle + +import tr#anslate from helper_sql import * +from debug import logger '''The singleCleaner class is a timer-driven thread that cleans data structures to free memory, resends messages when a remote node doesn't respond, and sends pong messages to keep connections alive if the network isn't busy. It cleans these data structures in memory: @@ -124,4 +128,20 @@ class singleCleaner(threading.Thread): objectType, streamNumber, payload, receivedTime = storedValue if streamNumber in shared.inventorySets: shared.inventorySets[streamNumber].add(hash) + + # Let us write out the knowNodes to disk if there is anything new to write out. + if shared.needToWriteKnownNodesToDisk: + shared.knownNodesLock.acquire() + output = open(shared.appdata + 'knownnodes.dat', 'wb') + try: + pickle.dump(shared.knownNodes, output) + output.close() + except Exception as err: + if "Errno 28" in str(err): + logger.fatal('(while receiveDataThread shared.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ') + shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) + if shared.daemon: + os._exit(0) + shared.knownNodesLock.release() + shared.needToWriteKnownNodesToDisk = False time.sleep(300) diff --git a/src/shared.py b/src/shared.py index 0c0173a3..22a6d890 100644 --- a/src/shared.py +++ b/src/shared.py @@ -69,6 +69,7 @@ numberOfPubkeysProcessed = 0 numberOfInventoryLookupsPerformed = 0 daemon = False inventorySets = {} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. +needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually. #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work.