From 40033d9e87d0bb821b34cbf6291c0d769d969276 Mon Sep 17 00:00:00 2001 From: Amos Bairn Date: Wed, 11 Sep 2013 23:04:16 -0700 Subject: [PATCH 1/7] add api method addChan This solves issue #484 --- src/bitmessagemain.py | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 88538297..413ae481 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -384,6 +384,70 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): ('getDeterministicAddress', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) return shared.apiAddressGeneratorReturnQueue.get() + elif method == 'addChan': + #get passphrase, addressVersionNumber, and streamNumber + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + passphrase, = params + address = '' + addressVersionNumber = 0 + streamNumber = 0 + label = '' + elif len(params) == 2: + passphrase, address = params + status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) + label = '' + elif len(params) == 3: + passphrase, addressVersionNumber, streamNumber = params + address = '' + label = '' + elif len(params) == 4: + passphrase, addressVersionNumber, streamNumber, label = params + address = '' + label = self._decode(label, "base64") + else: + raise APIError(0, 'Too many parameters!') + + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + passphrase = self._decode(passphrase, "base64") + if label == '': + label = '[chan] ' + passphrase + if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" + addressVersionNumber = 3 + if addressVersionNumber != 3: + 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: + raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') + + #create identity + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create chan %s.', passphrase) + shared.addressGeneratorQueue.put(('createChan', addressVersionNumber, streamNumber, label, passphrase)) + queueReturn = shared.apiAddressGeneratorReturnQueue.get() + if len(queueReturn) == 0: + raise APIError(24, 'Chan address already present.') + createdAddress = queueReturn[0] + if address == '': + address = createdAddress + elif createdAddress != address: + raise APIError(18, 'Chan name does not match address.') + + #add address to addressbook + address = addBMIfNotPresent(address) + self._verifyAddress(address) + queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) + if queryreturn != []: + raise APIError(16, 'You already have this address in your address book.') + + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Added chan %s with address %s and label %s." %(passphrase, address, label) elif method == 'getAllInboxMessages': queryreturn = sqlQuery( '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') From cdf4d5d75cbc663ad9ca47e9e631157f066aeec5 Mon Sep 17 00:00:00 2001 From: amos Date: Mon, 23 Sep 2013 21:05:11 -0700 Subject: [PATCH 2/7] Change the accepted address versions Update _verifyAddress to accept version 4. --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 3d7f5c73..4e473c85 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -166,7 +166,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(10, 'Address version number too high (or zero) in address: ' + address) raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) if addressVersionNumber < 2 or addressVersionNumber > 3: - raise APIError(11, 'The address version number currently must be 2 or 3. Others aren\'t supported. Check the address.') + raise APIError(11, 'The address version number currently must be 2, 3 or 4. Others aren\'t supported. Check the address.') if streamNumber != 1: raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the address.') From e878fb4c96f983b9b6fa426b3bc15a5aacd56a9c Mon Sep 17 00:00:00 2001 From: amos Date: Mon, 23 Sep 2013 22:35:20 -0700 Subject: [PATCH 3/7] Add api method decodeAddress Add decodeAddress as an api call. Like the addresses.decodeAddress function it return status, addressVersion, streamNumber, and ripe. ripe is base64 encoded. --- src/bitmessagemain.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 47925a46..2e7bb9af 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -818,6 +818,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): else: networkStatus = 'connectedAndReceivingIncomingConnections' return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': ')) + elif method == 'decodeAddress': + #decode an address + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + address, = params + status, addressVersion, streamNumber, ripe = decodeAddress(address) + return json.dumps({'status':status, 'addressVersion':addressVersion, + 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, + separators=(',', ': ')) else: raise APIError(20, 'Invalid method: %s' % method) From 379d27b5d8ce2593c4979e8219cb609de8ab44f6 Mon Sep 17 00:00:00 2001 From: amos Date: Mon, 23 Sep 2013 23:00:50 -0700 Subject: [PATCH 4/7] update address versions in addChan --- src/bitmessagemain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 76a4e53c..98760deb 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -418,9 +418,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if label == '': label = '[chan] ' + passphrase if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" - addressVersionNumber = 3 - if addressVersionNumber != 3: - raise APIError(2,'The address version number currently must be 3 (or 0 which means auto-select). ' + addressVersionNumber + ' isn\'t supported.') + addressVersionNumber = 4 + if addressVersionNumber != 3 and addressVersionNumber != 4: + raise APIError(2,'The address version number currently must be 3 or 4 (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: From 12edee4ac41a85485033730849094eaefeaf8687 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 25 Oct 2013 19:35:59 -0400 Subject: [PATCH 5/7] added API commands: createChan, joinChan, leaveChan, deleteAddress --- src/bitmessagemain.py | 139 +++++++++++++++++++++++++------------- src/class_singleWorker.py | 13 ++-- 2 files changed, 101 insertions(+), 51 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 29f1b812..2294d398 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -38,6 +38,8 @@ from debug import logger import helper_bootstrap import proofofwork +str_chan = '[chan]' + import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: @@ -150,8 +152,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def _decode(self, text, decode_type): try: return text.decode(decode_type) - except TypeError as e: - raise APIError(22, "Decode error - " + str(e)) + except Exception as e: + raise APIError(22, "Decode error - " + str(e) + ". Had trouble while decoding string: " + repr(text)) def _verifyAddress(self, address): status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) @@ -387,70 +389,113 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): ('getDeterministicAddress', addressVersionNumber, streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) return shared.apiAddressGeneratorReturnQueue.get() - elif method == 'addChan': - #get passphrase, addressVersionNumber, and streamNumber + + elif method == 'createChan': if len(params) == 0: raise APIError(0, 'I need parameters.') elif len(params) == 1: passphrase, = params - address = '' - addressVersionNumber = 0 - streamNumber = 0 - label = '' - elif len(params) == 2: - passphrase, address = params - status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) - label = '' - elif len(params) == 3: - passphrase, addressVersionNumber, streamNumber = params - address = '' - label = '' - elif len(params) == 4: - passphrase, addressVersionNumber, streamNumber, label = params - address = '' - label = self._decode(label, "base64") - else: - raise APIError(0, 'Too many parameters!') - + passphrase = self._decode(passphrase, "base64") if len(passphrase) == 0: raise APIError(1, 'The specified passphrase is blank.') - passphrase = self._decode(passphrase, "base64") - if label == '': - label = '[chan] ' + passphrase - if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" - addressVersionNumber = 4 - if addressVersionNumber != 3 and addressVersionNumber != 4: - raise APIError(2,'The address version number currently must be 3 or 4 (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: - raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) - #create identity + addressVersionNumber = 4 + streamNumber = 1 shared.apiAddressGeneratorReturnQueue.queue.clear() logger.debug('Requesting that the addressGenerator create chan %s.', passphrase) shared.addressGeneratorQueue.put(('createChan', addressVersionNumber, streamNumber, label, passphrase)) queueReturn = shared.apiAddressGeneratorReturnQueue.get() if len(queueReturn) == 0: - raise APIError(24, 'Chan address already present.') - createdAddress = queueReturn[0] - if address == '': - address = createdAddress - elif createdAddress != address: - raise APIError(18, 'Chan name does not match address.') + raise APIError(24, 'Chan address is already present.') + address = queueReturn[0] #add address to addressbook - address = addBMIfNotPresent(address) - self._verifyAddress(address) queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) - if queryreturn != []: - raise APIError(16, 'You already have this address in your address book.') + if queryreturn == []: + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return address + elif method == 'joinChan': + if len(params) < 2: + raise APIError(0, 'I need two parameters.') + elif len(params) == 2: + passphrase, suppliedAddress= params + passphrase = self._decode(passphrase, "base64") + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) - sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(suppliedAddress) + suppliedAddress = addBMIfNotPresent(suppliedAddress) + shared.apiAddressGeneratorReturnQueue.queue.clear() + shared.addressGeneratorQueue.put(('joinChan', suppliedAddress, label, passphrase)) + addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() + + if addressGeneratorReturnValue == 'chan name does not match address': + raise APIError(18, 'Chan name does not match address.') + if len(addressGeneratorReturnValue) == 0: + raise APIError(24, 'Chan address is already present.') + createdAddress = addressGeneratorReturnValue[0] + #add address to addressbook + queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", createdAddress) + if queryreturn == []: + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, createdAddress) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "success" + elif method == 'leaveChan': + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + if not shared.safeConfigGetBoolean(address, 'chan'): + raise APIError(25, 'Specified address is not a chan address. Use deleteAddress API call instead.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + sqlExecute('''DELETE FROM addressbook WHERE address=?''', address) shared.UISignalQueue.put(('rerenderInboxFromLabels','')) shared.UISignalQueue.put(('rerenderSentToLabels','')) shared.UISignalQueue.put(('rerenderAddressBook','')) - return "Added chan %s with address %s and label %s." %(passphrase, address, label) + return 'success' + + elif method == 'deleteAddress': + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.reloadMyAddressHashes() + return 'success' + elif method == 'getAllInboxMessages': queryreturn = sqlQuery( '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index b10e1919..2680f307 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -315,10 +315,15 @@ class singleWorker(threading.Thread): shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) shared.UISignalQueue.put(('updateStatusBar', '')) - shared.config.set( - myAddress, 'lastpubkeysendtime', str(int(time.time()))) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + try: + shared.config.set( + myAddress, 'lastpubkeysendtime', str(int(time.time()))) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + except: + # The user deleted the address out of the keys.dat file before this + # finished. + pass def sendBroadcast(self): queryreturn = sqlQuery( From 422f47fae3ed7ed9e30ca538f6bebcbbc08207f6 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 25 Oct 2013 19:49:18 -0400 Subject: [PATCH 6/7] Modify readme.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c164c03a..c725c3bc 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,10 @@ warrantless wiretapping programs. Development ---------- -If you plan to put a non-trivial amount of work into coding new features, it -is recommended that you first solicit feedback on the DevTalk pseudo-mailing -list: +Bitmessage is a collaborative project. You are welcome to submit pull requests +although if you plan to put a non-trivial amount of work into coding new +features, it is recommended that you first solicit feedback on the DevTalk +pseudo-mailing list: BM-2D9QKN4teYRvoq2fyzpiftPh9WP9qggtzh From 4ec91b6ed0de0d99fa82f771529c757498e63272 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 25 Oct 2013 19:57:06 -0400 Subject: [PATCH 7/7] modified addresses.decodeAddress so that API decodeAddress works properly --- src/addresses.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index 5b092ca5..5f666543 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -139,7 +139,7 @@ def decodeAddress(address): integer = decodeBase58(address) if integer == 0: status = 'invalidcharacters' - return status,0,0,0 + return status,0,0,"" #after converting to hex, the string will be prepended with a 0x and appended with a L hexdata = hex(integer)[2:-1] @@ -161,7 +161,7 @@ def decodeAddress(address): if checksum != sha.digest()[0:4]: status = 'checksumfailed' - return status,0,0,0 + return status,0,0,"" #else: # print 'checksum PASSED' @@ -172,11 +172,11 @@ def decodeAddress(address): if addressVersionNumber > 4: print 'cannot decode address version numbers this high' status = 'versiontoohigh' - return status,0,0,0 + return status,0,0,"" elif addressVersionNumber == 0: print 'cannot decode address version numbers of zero.' status = 'versiontoohigh' - return status,0,0,0 + return status,0,0,"" streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:]) #print streamNumber @@ -191,16 +191,16 @@ def decodeAddress(address): elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 18: return status,addressVersionNumber,streamNumber,'\x00\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 18: - return 'ripetooshort',0,0,0 + return 'ripetooshort',0,0,"" elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) > 20: - return 'ripetoolong',0,0,0 + return 'ripetoolong',0,0,"" else: - return 'otherproblem',0,0,0 + return 'otherproblem',0,0,"" elif addressVersionNumber == 4: if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) > 20: - return 'ripetoolong',0,0,0 + return 'ripetoolong',0,0,"" elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 4: - return 'ripetooshort',0,0,0 + return 'ripetooshort',0,0,"" else: x00string = '\x00' * (20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4])) return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]