From 9d3a0a160fe152e0d4978dbd95b05e909c4c5ef5 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sun, 11 Aug 2013 00:08:48 +0100 Subject: [PATCH] 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'])