Added some API commands for use with a mobile app #391

Merged
Atheros1 merged 10 commits from master into master 2013-08-09 20:25:14 +02:00
13 changed files with 266 additions and 110 deletions

View File

@ -738,6 +738,115 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': '))
data += ']}' data += ']}'
return 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': elif method == 'clientStatus':
return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList))
else: else:
@ -757,11 +866,8 @@ class singleAPI(threading.Thread):
se.register_introspection_functions() se.register_introspection_functions()
se.serve_forever() se.serve_forever()
selfInitiatedConnections = {}
# This is a list of current connections (the thread pointers at least) # This is a list of current connections (the thread pointers at least)
selfInitiatedConnections = {}
if shared.useVeryEasyProofOfWorkForTesting: if shared.useVeryEasyProofOfWorkForTesting:

View File

@ -2000,6 +2000,8 @@ class MyForm(QtGui.QMainWindow):
self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked())) self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked()))
shared.config.set('bitmessagesettings', 'startintray', str( shared.config.set('bitmessagesettings', 'startintray', str(
self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked())) 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 int(shared.config.get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()):
if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate(
@ -2996,6 +2998,8 @@ class settingsDialog(QtGui.QDialog):
shared.config.getboolean('bitmessagesettings', 'showtraynotifications')) shared.config.getboolean('bitmessagesettings', 'showtraynotifications'))
self.ui.checkBoxStartInTray.setChecked( self.ui.checkBoxStartInTray.setChecked(
shared.config.getboolean('bitmessagesettings', 'startintray')) shared.config.getboolean('bitmessagesettings', 'startintray'))
self.ui.checkBoxWillinglySendToMobile.setChecked(
shared.safeConfigGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
if shared.appdata == '': if shared.appdata == '':
self.ui.checkBoxPortableMode.setChecked(True) self.ui.checkBoxPortableMode.setChecked(True)
if 'darwin' in sys.platform: if 'darwin' in sys.platform:
@ -3290,6 +3294,7 @@ def run():
try: try:
translator.load("translations/bitmessage_" + str(locale.getdefaultlocale()[0])) translator.load("translations/bitmessage_" + str(locale.getdefaultlocale()[0]))
#translator.load("translations/bitmessage_fr_BE") # test French
except: except:
# The above is not compatible with all versions of OSX. # The above is not compatible with all versions of OSX.
translator.load("translations/bitmessage_en_US") # Default to english. translator.load("translations/bitmessage_en_US") # Default to english.

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'bitmessageui.ui' # 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 # by: PyQt4 UI code generator 4.10
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -26,7 +26,7 @@ except AttributeError:
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(775, 598) MainWindow.resize(795, 580)
icon = QtGui.QIcon() icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon) MainWindow.setWindowIcon(icon)
@ -422,7 +422,7 @@ class Ui_MainWindow(object):
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow) 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.menubar.setObjectName(_fromUtf8("menubar"))
self.menuFile = QtGui.QMenu(self.menubar) self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName(_fromUtf8("menuFile")) self.menuFile.setObjectName(_fromUtf8("menuFile"))
@ -539,7 +539,7 @@ class Ui_MainWindow(object):
self.textEditMessage.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" self.textEditMessage.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n" "p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Ubuntu\'; font-size:9pt; font-weight:400; font-style:normal;\">\n" "</style></head><body style=\" font-family:\'Droid Sans\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<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>", None)) "<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>", None))
self.label.setText(_translate("MainWindow", "To:", None)) self.label.setText(_translate("MainWindow", "To:", None))
self.label_2.setText(_translate("MainWindow", "From:", None)) self.label_2.setText(_translate("MainWindow", "From:", None))

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>775</width> <width>795</width>
<height>598</height> <height>580</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -257,7 +257,7 @@
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt; <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-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';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;p style=&quot;-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';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
@ -1010,8 +1010,8 @@ p, li { white-space: pre-wrap; }
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>775</width> <width>795</width>
<height>21</height> <height>27</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">

View File

@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'newchandialog.ui' # Form implementation generated from reading ui file 'newchandialog.ui'
# #
# Created: Mon Jul 22 01:05:35 2013 # Created: Wed Aug 7 16:51:29 2013
# by: PyQt4 UI code generator 4.10.2 # by: PyQt4 UI code generator 4.10
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -26,7 +26,7 @@ except AttributeError:
class Ui_newChanDialog(object): class Ui_newChanDialog(object):
def setupUi(self, newChanDialog): def setupUi(self, newChanDialog):
newChanDialog.setObjectName(_fromUtf8("newChanDialog")) newChanDialog.setObjectName(_fromUtf8("newChanDialog"))
newChanDialog.resize(530, 422) newChanDialog.resize(553, 422)
newChanDialog.setMinimumSize(QtCore.QSize(0, 0)) newChanDialog.setMinimumSize(QtCore.QSize(0, 0))
self.formLayout = QtGui.QFormLayout(newChanDialog) self.formLayout = QtGui.QFormLayout(newChanDialog)
self.formLayout.setObjectName(_fromUtf8("formLayout")) 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.radioButtonJoinChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxJoinChan.setShown)
QtCore.QObject.connect(self.radioButtonCreateChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxCreateChan.setShown) QtCore.QObject.connect(self.radioButtonCreateChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxCreateChan.setShown)
QtCore.QMetaObject.connectSlotsByName(newChanDialog) 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): def retranslateUi(self, newChanDialog):
newChanDialog.setWindowTitle(_translate("newChanDialog", "Dialog", None)) newChanDialog.setWindowTitle(_translate("newChanDialog", "Dialog", None))
self.radioButtonCreateChan.setText(_translate("newChanDialog", "Create a new chan", None)) self.radioButtonCreateChan.setText(_translate("newChanDialog", "Create a new chan", None))
self.radioButtonJoinChan.setText(_translate("newChanDialog", "Join a chan", None)) self.radioButtonJoinChan.setText(_translate("newChanDialog", "Join a chan", None))
self.groupBoxCreateChan.setTitle(_translate("newChanDialog", "Create 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", "<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>", None))
self.label_5.setText(_translate("newChanDialog", "Chan name:", None)) self.label_5.setText(_translate("newChanDialog", "Chan name:", None))
self.groupBoxJoinChan.setTitle(_translate("newChanDialog", "Join a chan", None)) self.groupBoxJoinChan.setTitle(_translate("newChanDialog", "Join a chan", None))
self.label.setText(_translate("newChanDialog", "<html><head/><body><p>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.</p><p>Chans are experimental and completely unmoderatable.</p></body></html>", None)) self.label.setText(_translate("newChanDialog", "<html><head/><body><p>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.</p><p>Chans are experimental and completely unmoderatable.</p></body></html>", None))

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>530</width> <width>553</width>
<height>422</height> <height>422</height>
</rect> </rect>
</property> </property>
@ -46,7 +46,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>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.</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -130,6 +130,14 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>radioButtonJoinChan</tabstop>
<tabstop>radioButtonCreateChan</tabstop>
<tabstop>lineEditChanNameCreate</tabstop>
<tabstop>lineEditChanNameJoin</tabstop>
<tabstop>lineEditChanBitmessageAddress</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'settings.ui' # Form implementation generated from reading ui file 'settings.ui'
# #
# Created: Fri Jul 12 12:37:53 2013 # Created: Wed Aug 7 16:58:45 2013
# by: PyQt4 UI code generator 4.10.1 # by: PyQt4 UI code generator 4.10
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -26,7 +26,7 @@ except AttributeError:
class Ui_settingsDialog(object): class Ui_settingsDialog(object):
def setupUi(self, settingsDialog): def setupUi(self, settingsDialog):
settingsDialog.setObjectName(_fromUtf8("settingsDialog")) settingsDialog.setObjectName(_fromUtf8("settingsDialog"))
settingsDialog.resize(445, 343) settingsDialog.resize(462, 343)
self.gridLayout = QtGui.QGridLayout(settingsDialog) self.gridLayout = QtGui.QGridLayout(settingsDialog)
self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) self.buttonBox = QtGui.QDialogButtonBox(settingsDialog)
@ -41,33 +41,36 @@ class Ui_settingsDialog(object):
self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface")) self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface"))
self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface) self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface)
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) 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 = QtGui.QLabel(self.tabUserInterface)
self.labelSettingsNote.setText(_fromUtf8("")) self.labelSettingsNote.setText(_fromUtf8(""))
self.labelSettingsNote.setWordWrap(True) self.labelSettingsNote.setWordWrap(True)
self.labelSettingsNote.setObjectName(_fromUtf8("labelSettingsNote")) 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) 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.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8(""))
self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings = QtGui.QWidget()
self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings"))
@ -252,12 +255,13 @@ class Ui_settingsDialog(object):
def retranslateUi(self, settingsDialog): def retranslateUi(self, settingsDialog):
settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) 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.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.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.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.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None))
self.groupBox.setTitle(_translate("settingsDialog", "Listening port", None)) self.groupBox.setTitle(_translate("settingsDialog", "Listening port", None))
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>445</width> <width>462</width>
<height>343</height> <height>343</height>
</rect> </rect>
</property> </property>
@ -37,13 +37,29 @@
<string>User Interface</string> <string>User Interface</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0"> <item row="7" column="0">
<widget class="QCheckBox" name="checkBoxStartOnLogon"> <widget class="QLabel" name="labelSettingsNote">
<property name="text"> <property name="text">
<string>Start Bitmessage on user login</string> <string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="checkBoxStartInTray"> <widget class="QCheckBox" name="checkBoxStartInTray">
<property name="text"> <property name="text">
@ -51,6 +67,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxShowTrayNotifications">
<property name="text">
<string>Show notification when message received</string>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="checkBoxMinimizeToTray"> <widget class="QCheckBox" name="checkBoxMinimizeToTray">
<property name="text"> <property name="text">
@ -61,20 +84,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxShowTrayNotifications">
<property name="text">
<string>Show notification when message received</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxPortableMode">
<property name="text">
<string>Run in Portable Mode</string>
</property>
</widget>
</item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
@ -85,28 +94,26 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="0" column="0">
<widget class="QLabel" name="labelSettingsNote"> <widget class="QCheckBox" name="checkBoxStartOnLogon">
<property name="text"> <property name="text">
<string/> <string>Start Bitmessage on user login</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="4" column="0">
<spacer name="verticalSpacer_2"> <widget class="QCheckBox" name="checkBoxPortableMode">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>Run in Portable Mode</string>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
<size> </item>
<width>20</width> <item row="6" column="0">
<height>40</height> <widget class="QCheckBox" name="checkBoxWillinglySendToMobile">
</size> <property name="text">
<string>Willingly include unencrypted destination address when sending to a mobile device</string>
</property> </property>
</spacer> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -33,9 +33,9 @@ class singleCleaner(threading.Thread):
for hash, storedValue in shared.inventory.items(): for hash, storedValue in shared.inventory.items():
objectType, streamNumber, payload, receivedTime = storedValue objectType, streamNumber, payload, receivedTime = storedValue
if int(time.time()) - 3600 > receivedTime: if int(time.time()) - 3600 > receivedTime:
t = (hash, objectType, streamNumber, payload, receivedTime) t = (hash, objectType, streamNumber, payload, receivedTime,'')
shared.sqlSubmitQueue.put( shared.sqlSubmitQueue.put(
'''INSERT INTO inventory VALUES (?,?,?,?,?)''') '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''')
shared.sqlSubmitQueue.put(t) shared.sqlSubmitQueue.put(t)
shared.sqlReturnQueue.get() shared.sqlReturnQueue.get()
del shared.inventory[hash] del shared.inventory[hash]

View File

@ -9,6 +9,7 @@ import proofofwork
import sys import sys
from class_addressGenerator import pointMult from class_addressGenerator import pointMult
import tr import tr
from debug import logger
# This thread, of which there is only one, does the heavy lifting: # This thread, of which there is only one, does the heavy lifting:
# calculating POWs. # calculating POWs.
@ -517,6 +518,15 @@ class singleWorker(threading.Thread):
pubkeyPayload[readPosition:readPosition + 10]) pubkeyPayload[readPosition:readPosition + 10])
readPosition += streamNumberLength readPosition += streamNumberLength
behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4] 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 readPosition += 4 # to bypass the bitfield of behaviors
# pubSigningKeyBase256 = # pubSigningKeyBase256 =
# pubkeyPayload[readPosition:readPosition+64] #We don't use this # pubkeyPayload[readPosition:readPosition+64] #We don't use this
@ -663,6 +673,10 @@ class singleWorker(threading.Thread):
with shared.printLock: with shared.printLock:
print 'Not bothering to generate ackdata because we are sending to a chan.' print 'Not bothering to generate ackdata because we are sending to a chan.'
fullAckPayload = '' 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: else:
fullAckPayload = self.generateFullAckMessage( 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. 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.

View File

@ -46,7 +46,7 @@ class sqlThread(threading.Thread):
self.cur.execute( self.cur.execute(
'''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' ) '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' )
self.cur.execute( 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( self.cur.execute(
'''CREATE TABLE knownnodes (timelastseen int, stream int, services blob, host blob, port blob, UNIQUE(host, stream, port) ON CONFLICT REPLACE)''' ) '''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 # 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)''') '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
self.cur.execute( self.cur.execute(
'''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) '''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',?)''', ( self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
int(time.time()),)) int(time.time()),))
self.conn.commit() self.conn.commit()
@ -190,6 +190,19 @@ class sqlThread(threading.Thread):
if not shared.config.has_option('bitmessagesettings', 'sockslisten'): if not shared.config.has_option('bitmessagesettings', 'sockslisten'):
shared.config.set('bitmessagesettings', 'sockslisten', 'false') 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: try:
testpayload = '\x00\x00' testpayload = '\x00\x00'
t = ('1234', testpayload, '12345678', 'no') t = ('1234', testpayload, '12345678', 'no')

View File

@ -5,19 +5,10 @@
import sqlite3 import sqlite3
from time import strftime, localtime from time import strftime, localtime
import sys import sys
import shared
import string
APPNAME = "PyBitmessage" appdata = shared.lookupAppdataFolder()
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 + "/"))
conn = sqlite3.connect( appdata + 'messages.dat' ) conn = sqlite3.connect( appdata + 'messages.dat' )
conn.text_factory = str conn.text_factory = str

View File

@ -101,10 +101,8 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber):
random.seed() random.seed()
payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf
userAgent = '/PyBitmessage:' + shared.softwareVersion + \ userAgent = '/PyBitmessage:' + shared.softwareVersion + '/'
'/' # Length of userAgent must be less than 253. payload += encodeVarint(len(userAgent))
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.
payload += userAgent payload += userAgent
payload += encodeVarint( payload += encodeVarint(
1) # The number of streams about which I care. PyBitmessage currently only supports 1 per connection. 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) fullString = arithmetic.changebase(WIFstring,58,256)
privkey = fullString[:-4] privkey = fullString[:-4]
if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[: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 ' logger.critical('Major problem! When trying to decode one of your private keys, the checksum '
'failed. Here is the PRIVATE key: %s\n' % str(WIFstring)) 'failed. Here is the PRIVATE key: %s' % str(WIFstring))
return "" return ""
else: else:
#checksum passed #checksum passed
if privkey[0] == '\x80': if privkey[0] == '\x80':
return privkey[1:] return privkey[1:]
else: 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 ' '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 "" return ""
@ -243,8 +241,7 @@ def reloadMyAddressHashes():
myAddressesByHash[hash] = addressInKeysFile myAddressesByHash[hash] = addressInKeysFile
else: else:
logger.error('Error in reloadMyAddressHashes: Can\'t handle address ' logger.error('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
'versions other than 2 or 3.\n')
if not keyfileSecure: if not keyfileSecure:
fixSensitiveFilePermissions(appdata + 'keys.dat', hasEnabledKeys) fixSensitiveFilePermissions(appdata + 'keys.dat', hasEnabledKeys)
@ -320,8 +317,8 @@ def flushInventory():
sqlLock.acquire() sqlLock.acquire()
for hash, storedValue in inventory.items(): for hash, storedValue in inventory.items():
objectType, streamNumber, payload, receivedTime = storedValue objectType, streamNumber, payload, receivedTime = storedValue
t = (hash,objectType,streamNumber,payload,receivedTime) t = (hash,objectType,streamNumber,payload,receivedTime,'')
sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?)''') sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''')
sqlSubmitQueue.put(t) sqlSubmitQueue.put(t)
sqlReturnQueue.get() sqlReturnQueue.get()
del inventory[hash] del inventory[hash]
@ -384,6 +381,12 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys):
logger.exception('Keyfile permissions could not be fixed.') logger.exception('Keyfile permissions could not be fixed.')
raise 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']) Peer = collections.namedtuple('Peer', ['host', 'port'])
helper_startup.loadConfig() helper_startup.loadConfig()