From 17533237fe9420579e5a87d025ffa093a5de9478 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 2 Aug 2013 18:35:31 -0400 Subject: [PATCH 01/13] some initial work done to support particular android client --- src/bitmessagemain.py | 67 ++++++++++++++++++++++++++++++++++++ src/bitmessageqt/__init__.py | 1 + src/class_sqlThread.py | 10 +++++- src/message_data_reader.py | 41 +++++++++++++++------- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b156cd0d..b63cc2b3 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -738,6 +738,73 @@ 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. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + encryptedPayload, = params + 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(( + streamNumber, '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. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + payload, = params + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + shared.inventory[inventoryHash] = ( + objectType, streamNumber, 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 == 'getMessageDataByDestinationRIPEHash': + # Method will eventually be used by a particular Android app. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + hash, = params + #if len(hash) != 40: + # return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + print repr(hash) + hash = hash.decode('hex') + print repr(hash) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''PRAGMA case_sensitive_like = true''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + + hash = string.replace(hash,'e','ee') + hash = string.replace(hash,'%','e%') + hash = string.replace(hash,'_','e_') + print 'searching for hash:', repr(hash) + parameters = ('%'+ hash + '%',) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''') + 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 == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5dd4c0b5..a9a7179a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3290,6 +3290,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/class_sqlThread.py b/src/class_sqlThread.py index e07850e4..48be5e0d 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -189,7 +189,15 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'sockslisten'): shared.config.set('bitmessagesettings', 'sockslisten', 'false') - + + # Some prewritten code for future use whenever we need to modify the database + """item = '''SELECT value FROM settings WHERE key='version';''' + parameters = '' + self.cur.execute(item, parameters) + if self.cur.fetchall()[0][0] == 1: + do something + increment the version to 2""" + try: testpayload = '\x00\x00' t = ('1234', testpayload, '12345678', 'no') diff --git a/src/message_data_reader.py b/src/message_data_reader.py index c600935d..f6102e02 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 @@ -71,6 +62,29 @@ def readInventory(): hash, objecttype, streamnumber, payload, receivedtime = row print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') +def readInventory2(): + searchValue = ' ' + + item = '''PRAGMA case_sensitive_like = true ''' + parameters = '' + cur.execute(item, parameters) + + searchValue = string.replace(searchValue,'e','ee') + searchValue = string.replace(searchValue,'%','e%') + searchValue = string.replace(searchValue,'_','e_') + + print 'Printing subset of inventory table:' + item = '''SELECT * FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''' + parameters = ('%'+ searchValue + '%',) + print repr(parameters), len(parameters[0]) + cur.execute(item, parameters) + output = cur.fetchall() + print 'Number of results:', len(output) + for row in output[:20]: + hash, objecttype, streamnumber, payload, receivedtime = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + print 'done' + def takeInboxMessagesOutOfTrash(): item = '''update inbox set folder='inbox' where folder='trash' ''' @@ -107,12 +121,13 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -readInbox() +#readInbox() #readSent() #readPubkeys() #readSubscriptions() #readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. +readInventory2() From 9460712a591118bc1bb923f3ed7f6c251898cc4c Mon Sep 17 00:00:00 2001 From: Gregor Robinson Date: Mon, 5 Aug 2013 22:06:46 +0200 Subject: [PATCH 02/13] 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 c5442029b5515750f2bd56210c825d6cfd6e9534 Mon Sep 17 00:00:00 2001 From: merlink Date: Mon, 5 Aug 2013 22:29:06 +0200 Subject: [PATCH 03/13] 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 86383f0a9fcea4b6403020617e9c620836224f7f Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 10:37:31 +0200 Subject: [PATCH 04/13] 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 084f67b10f83a38318effbfa4823c34f2c1b2fe2 Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 13:23:56 +0200 Subject: [PATCH 05/13] 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 d51fe37a66fbce2d658a7725499bf4f214c5647d Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 6 Aug 2013 13:19:26 -0400 Subject: [PATCH 06/13] added requested API commands for mobile device --- src/bitmessagemain.py | 55 ++++++++++++++++++++++---------------- src/class_singleCleaner.py | 4 +-- src/class_sqlThread.py | 19 ++++++++----- src/message_data_reader.py | 28 ++++++++++--------- src/shared.py | 7 +++-- 5 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b63cc2b3..82bcd3de 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -747,6 +747,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) != 1: return 'API Error 0000: I need 1 parameter!' encryptedPayload, = params + encryptedPayload = encryptedPayload.decode('hex') inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' shared.inventory[inventoryHash] = ( @@ -754,15 +755,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + 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. + # rest of the Bitmessage network as if it had generated the pubkey object itself. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' payload, = params + payload = payload.decode('hex') inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' shared.inventory[inventoryHash] = ( @@ -772,30 +774,40 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.broadcastToSendDataQueues(( streamNumber, 'sendinv', inventoryHash)) elif method == 'getMessageDataByDestinationRIPEHash': - # Method will eventually be used by a particular Android app. + # Method will eventually be used by a particular Android app to + # select relevant messages. + if len(params) != 1: return 'API Error 0000: I need 1 parameter!' - hash, = params - #if len(hash) != 40: - # return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' - print repr(hash) - hash = hash.decode('hex') - print repr(hash) - with shared.sqlLock: - shared.sqlSubmitQueue.put('''PRAGMA case_sensitive_like = true''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() + 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') - hash = string.replace(hash,'e','ee') - hash = string.replace(hash,'%','e%') - hash = string.replace(hash,'_','e_') - print 'searching for hash:', repr(hash) - parameters = ('%'+ hash + '%',) + # 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 payload FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''') + 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 @@ -824,11 +836,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/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_sqlThread.py b/src/class_sqlThread.py index 48be5e0d..625b2b2f 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() @@ -190,13 +190,18 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'sockslisten'): shared.config.set('bitmessagesettings', 'sockslisten', 'false') - # Some prewritten code for future use whenever we need to modify the database - """item = '''SELECT value FROM settings WHERE key='version';''' + # 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 self.cur.fetchall()[0][0] == 1: - do something - increment the version to 2""" + 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' diff --git a/src/message_data_reader.py b/src/message_data_reader.py index f6102e02..35b2441a 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -54,16 +54,16 @@ def readPubkeys(): def readInventory(): print 'Printing everything in inventory table:' - item = '''select hash, objecttype, streamnumber, payload, receivedtime from inventory''' + item = '''select hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage from inventory where objecttype = 'msg' ''' parameters = '' cur.execute(item, parameters) output = cur.fetchall() - for row in output: - hash, objecttype, streamnumber, payload, receivedtime = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + for row in output[:50]: + hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', 'first20bytesofencryptedmessage:', first20bytesofencryptedmessage.encode('hex'), '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') def readInventory2(): - searchValue = ' ' + searchValue = ' ' item = '''PRAGMA case_sensitive_like = true ''' parameters = '' @@ -74,15 +74,17 @@ def readInventory2(): searchValue = string.replace(searchValue,'_','e_') print 'Printing subset of inventory table:' - item = '''SELECT * FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''' - parameters = ('%'+ searchValue + '%',) - print repr(parameters), len(parameters[0]) + item = '''SELECT substr(payload,20) FROM inventory''' + #parameters = ('%'+ searchValue + '%',) + #print repr(parameters), len(parameters[0]) + parameters = '' cur.execute(item, parameters) output = cur.fetchall() print 'Number of results:', len(output) - for row in output[:20]: - hash, objecttype, streamnumber, payload, receivedtime = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + for row in output[:100]: + print row + #hash, objecttype, streamnumber, payload, receivedtime = row + #print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') print 'done' @@ -125,9 +127,9 @@ def vacuum(): #readSent() #readPubkeys() #readSubscriptions() -#readInventory() +readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. -readInventory2() +#readInventory2() diff --git a/src/shared.py b/src/shared.py index 214c124f..925459f7 100644 --- a/src/shared.py +++ b/src/shared.py @@ -243,8 +243,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 +319,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] From ecef8f93b5cfe8b92bf075131d40aca4e681a568 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 7 Aug 2013 15:15:49 -0400 Subject: [PATCH 07/13] minor code refactoring --- src/shared.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shared.py b/src/shared.py index 925459f7..09a2c26c 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. From 854b7394311172afbb67405d50abeb703aaea234 Mon Sep 17 00:00:00 2001 From: Lucretiel Date: Thu, 8 Aug 2013 03:52:47 -0400 Subject: [PATCH 08/13] The createDefaultKnownNodes function now uses a with statement --- src/defaultKnownNodes.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 52e31fff..d4611099 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): From cdec4ad506cda3fe2d832ccda98bbb36806ccb7a Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 15:37:48 -0400 Subject: [PATCH 09/13] Added option in settings menu to allow sending to mobile --- src/bitmessageqt/__init__.py | 4 ++ src/bitmessageqt/newchandialog.py | 13 ++++-- src/bitmessageqt/newchandialog.ui | 12 ++++- src/bitmessageqt/settings.py | 60 +++++++++++++------------ src/bitmessageqt/settings.ui | 75 +++++++++++++++++-------------- src/class_singleWorker.py | 14 ++++++ src/shared.py | 14 ++++-- 7 files changed, 120 insertions(+), 72 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a9a7179a..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: 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_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/shared.py b/src/shared.py index 09a2c26c..db5a9810 100644 --- a/src/shared.py +++ b/src/shared.py @@ -201,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 "" @@ -367,6 +367,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 176340c22d3dc0f8493ea26c7d942bf4fb0110a7 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 17:50:12 -0400 Subject: [PATCH 10/13] added api command: getPubkeyByHash --- src/bitmessagemain.py | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 4e6b327f..cae8daeb 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -743,11 +743,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # 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. + # 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] = ( @@ -760,22 +761,32 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # 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. + # 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, streamNumber, payload, int(time.time())) + 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 == 'getMessageDataByDestinationRIPEHash': + elif method == 'getMessageDataByDestinationHash': # Method will eventually be used by a particular Android app to - # select relevant messages. + # select relevant messages. Do not yet add this to the api + # doc. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' @@ -816,7 +827,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 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: From 035d3af61202f0c3acdd7c32bc7d6f0fa6fe0e26 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 17:55:15 -0400 Subject: [PATCH 11/13] revert message_data_reader.py to the state it was in before I used it for debugging --- src/message_data_reader.py | 38 ++++++-------------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/src/message_data_reader.py b/src/message_data_reader.py index 35b2441a..084ef553 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -54,38 +54,13 @@ def readPubkeys(): def readInventory(): print 'Printing everything in inventory table:' - item = '''select hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage from inventory where objecttype = 'msg' ''' + item = '''select hash, objecttype, streamnumber, payload, receivedtime from inventory''' parameters = '' cur.execute(item, parameters) output = cur.fetchall() - for row in output[:50]: - hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', 'first20bytesofencryptedmessage:', first20bytesofencryptedmessage.encode('hex'), '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') - -def readInventory2(): - searchValue = ' ' - - item = '''PRAGMA case_sensitive_like = true ''' - parameters = '' - cur.execute(item, parameters) - - searchValue = string.replace(searchValue,'e','ee') - searchValue = string.replace(searchValue,'%','e%') - searchValue = string.replace(searchValue,'_','e_') - - print 'Printing subset of inventory table:' - item = '''SELECT substr(payload,20) FROM inventory''' - #parameters = ('%'+ searchValue + '%',) - #print repr(parameters), len(parameters[0]) - parameters = '' - cur.execute(item, parameters) - output = cur.fetchall() - print 'Number of results:', len(output) - for row in output[:100]: - print row - #hash, objecttype, streamnumber, payload, receivedtime = row - #print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') - print 'done' + for row in output: + hash, objecttype, streamnumber, payload, receivedtime = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') def takeInboxMessagesOutOfTrash(): @@ -123,13 +98,12 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -#readInbox() +readInbox() #readSent() #readPubkeys() #readSubscriptions() -readInventory() +#readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. -#readInventory2() From d3b9791442f800e85a7d450b24dfcdd65121ae45 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 9 Aug 2013 12:12:57 -0400 Subject: [PATCH 12/13] Fix problem with pull request #388 --- src/defaultKnownNodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index d4611099..f481581c 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -37,7 +37,7 @@ def createDefaultKnownNodes(appdata): #print stream1 #print allKnownNodes - with open(appdata + 'knownnodes.dat', 'wb) as output: + with open(appdata + 'knownnodes.dat', 'wb') as output: # Pickle dictionary using protocol 0. pickle.dump(allKnownNodes, output) From 1a43402ad2cacca9a674526bf20c01e8d1f5e86c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 9 Aug 2013 14:21:24 -0400 Subject: [PATCH 13/13] Adjusted size of main window back to the way it was --- src/bitmessageqt/bitmessageui.py | 8 ++++---- src/bitmessageqt/bitmessageui.ui | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) 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