From 3063c256d4dee650fe66a8a8b937ffce7de6b8f6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 3 Aug 2013 12:45:15 +0100 Subject: [PATCH 01/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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 0132db33dca24840fce680050fbbe77910715b38 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 24 Aug 2013 19:40:48 -0400 Subject: [PATCH 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 b5f42d75496f9119070fe679fb096ddb20b374df Mon Sep 17 00:00:00 2001 From: Joshua Noble Date: Mon, 26 Aug 2013 22:29:57 -0400 Subject: [PATCH 39/43] 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 0d5f2680d404125204156d8a2a2bf81faa94d62f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 27 Aug 2013 22:29:39 -0400 Subject: [PATCH 40/43] 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 41/43] 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 42/43] 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 43/43] 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 Русский