diff --git a/Makefile b/Makefile index 9de3e463..ac602a3f 100755 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ APP=pybitmessage -VERSION=0.3.3-2 +VERSION=0.3.4 DEST_SHARE=$(DESTDIR)/usr/share DEST_APP=$(DEST_SHARE)/$(APP) @@ -18,6 +18,7 @@ install: mkdir -m 755 -p $(DEST_APP)/pyelliptic mkdir -m 755 -p $(DEST_APP)/socks mkdir -m 755 -p $(DEST_APP)/bitmessageqt + mkdir -m 755 -p $(DEST_APP)/translations mkdir -m 755 -p $(DEST_SHARE)/pixmaps mkdir -m 755 -p $(DEST_SHARE)/icons mkdir -m 755 -p $(DEST_SHARE)/icons/hicolor @@ -35,6 +36,7 @@ install: install -m 644 src/pyelliptic/*.py $(DEST_APP)/pyelliptic install -m 644 src/socks/*.py $(DEST_APP)/socks install -m 644 src/bitmessageqt/*.py $(DEST_APP)/bitmessageqt + install -m 644 src/translations/*.qm $(DEST_APP)/translations install -m 755 debian/pybm $(DESTDIR)/usr/bin/$(APP) install -m 644 desktop/$(APP).desktop $(DEST_SHARE)/applications/$(APP).desktop diff --git a/debian.sh b/debian.sh index 42817522..4b9410ed 100755 --- a/debian.sh +++ b/debian.sh @@ -7,8 +7,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.2 -VERSION=0.3.3-2 +PREV_VERSION=0.3.3 +VERSION=0.3.4 ARCH_TYPE=all #update version numbers automatically - so you don't have to diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 74f48d1a..c4c2b6db 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -706,11 +706,6 @@ if __name__ == "__main__": signal.signal(signal.SIGINT, helper_generic.signal_handler) # signal.signal(signal.SIGINT, signal.SIG_DFL) - # Check the Major version, the first element in the array - if sqlite3.sqlite_version_info[0] < 3: - print 'This program requires sqlite version 3 or higher because 2 and lower cannot store NULL values. I see version:', sqlite3.sqlite_version_info - os._exit(0) - helper_startup.loadConfig() helper_bootstrap.knownNodes() diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e11b537a..ce477836 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2844,6 +2844,16 @@ class NewAddressDialog(QtGui.QDialog): self.ui.groupBoxDeterministic.setHidden(True) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +class NewChanDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_NewChanDialog() + self.ui.setupUi(self) + self.parent = parent + self.ui.groupBoxCreateChan.setHidden(True) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + class iconGlossaryDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py new file mode 100644 index 00000000..5c5e11f2 --- /dev/null +++ b/src/bitmessageqt/newchandialog.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'newchandialog.ui' +# +# Created: Tue Jun 25 17:03:01 2013 +# by: PyQt4 UI code generator 4.10.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_NewChanDialog(object): + def setupUi(self, NewChanDialog): + NewChanDialog.setObjectName(_fromUtf8("NewChanDialog")) + NewChanDialog.resize(447, 441) + self.formLayout = QtGui.QFormLayout(NewChanDialog) + self.formLayout.setObjectName(_fromUtf8("formLayout")) + self.radioButtonCreateChan = QtGui.QRadioButton(NewChanDialog) + self.radioButtonCreateChan.setObjectName(_fromUtf8("radioButtonCreateChan")) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.radioButtonCreateChan) + self.radioButtonJoinChan = QtGui.QRadioButton(NewChanDialog) + self.radioButtonJoinChan.setChecked(True) + self.radioButtonJoinChan.setObjectName(_fromUtf8("radioButtonJoinChan")) + self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.radioButtonJoinChan) + self.groupBoxJoinChan = QtGui.QGroupBox(NewChanDialog) + self.groupBoxJoinChan.setObjectName(_fromUtf8("groupBoxJoinChan")) + self.gridLayout_2 = QtGui.QGridLayout(self.groupBoxJoinChan) + self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) + self.label = QtGui.QLabel(self.groupBoxJoinChan) + self.label.setWordWrap(True) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1) + self.label_2 = QtGui.QLabel(self.groupBoxJoinChan) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1) + self.lineEditChanNameJoin = QtGui.QLineEdit(self.groupBoxJoinChan) + self.lineEditChanNameJoin.setObjectName(_fromUtf8("lineEditChanNameJoin")) + self.gridLayout_2.addWidget(self.lineEditChanNameJoin, 2, 0, 1, 1) + self.label_3 = QtGui.QLabel(self.groupBoxJoinChan) + self.label_3.setObjectName(_fromUtf8("label_3")) + self.gridLayout_2.addWidget(self.label_3, 3, 0, 1, 1) + self.lineEditChanBitmessageAddress = QtGui.QLineEdit(self.groupBoxJoinChan) + self.lineEditChanBitmessageAddress.setObjectName(_fromUtf8("lineEditChanBitmessageAddress")) + self.gridLayout_2.addWidget(self.lineEditChanBitmessageAddress, 4, 0, 1, 1) + self.formLayout.setWidget(3, QtGui.QFormLayout.SpanningRole, self.groupBoxJoinChan) + self.groupBoxCreateChan = QtGui.QGroupBox(NewChanDialog) + self.groupBoxCreateChan.setObjectName(_fromUtf8("groupBoxCreateChan")) + self.gridLayout = QtGui.QGridLayout(self.groupBoxCreateChan) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.label_4 = QtGui.QLabel(self.groupBoxCreateChan) + self.label_4.setWordWrap(True) + self.label_4.setObjectName(_fromUtf8("label_4")) + self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.label_5 = QtGui.QLabel(self.groupBoxCreateChan) + self.label_5.setObjectName(_fromUtf8("label_5")) + self.gridLayout.addWidget(self.label_5, 1, 0, 1, 1) + self.lineEditChanNameCreate = QtGui.QLineEdit(self.groupBoxCreateChan) + self.lineEditChanNameCreate.setObjectName(_fromUtf8("lineEditChanNameCreate")) + self.gridLayout.addWidget(self.lineEditChanNameCreate, 2, 0, 1, 1) + self.formLayout.setWidget(2, QtGui.QFormLayout.SpanningRole, self.groupBoxCreateChan) + self.buttonBox = QtGui.QDialogButtonBox(NewChanDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.formLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.buttonBox) + + self.retranslateUi(NewChanDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), NewChanDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), NewChanDialog.reject) + 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) + + 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.groupBoxJoinChan.setTitle(_translate("NewChanDialog", "Join a chan", None)) + self.label.setText(_translate("NewChanDialog", "

A chan is a set of encryption keys that is shared by a group of people. The keys and bitmessage address used by a chan is generated from a human-friendly word or phrase (the chan name).

Chans are experimental and are unmoderatable.

", None)) + self.label_2.setText(_translate("NewChanDialog", "Chan name:", None)) + self.label_3.setText(_translate("NewChanDialog", "Chan bitmessage address:", 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_5.setText(_translate("NewChanDialog", "Chan name:", None)) + diff --git a/src/bitmessageqt/newchandialog.ui b/src/bitmessageqt/newchandialog.ui new file mode 100644 index 00000000..2e6e4657 --- /dev/null +++ b/src/bitmessageqt/newchandialog.ui @@ -0,0 +1,181 @@ + + + NewChanDialog + + + + 0 + 0 + 447 + 441 + + + + Dialog + + + + + + Create a new chan + + + + + + + Join a chan + + + true + + + + + + + Join a chan + + + + + + <html><head/><body><p>A chan is a set of encryption keys that is shared by a group of people. The keys and bitmessage address used by a chan is generated from a human-friendly word or phrase (the chan name).</p><p>Chans are experimental and are unmoderatable.</p></body></html> + + + true + + + + + + + Chan name: + + + + + + + + + + Chan bitmessage address: + + + + + + + + + + + + + Create a chan + + + + + + 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. + + + true + + + + + + + Chan name: + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NewChanDialog + accept() + + + 428 + 454 + + + 157 + 274 + + + + + buttonBox + rejected() + NewChanDialog + reject() + + + 430 + 460 + + + 286 + 274 + + + + + radioButtonJoinChan + toggled(bool) + groupBoxJoinChan + setShown(bool) + + + 74 + 49 + + + 96 + 227 + + + + + radioButtonCreateChan + toggled(bool) + groupBoxCreateChan + setShown(bool) + + + 72 + 28 + + + 65 + 92 + + + + + diff --git a/src/build_osx.py b/src/build_osx.py index db004769..aac551b9 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -14,7 +14,7 @@ from setuptools import setup # @UnresolvedImport name = "Bitmessage" mainscript = 'bitmessagemain.py' -version = "0.3.3" +version = "0.3.4" if sys.platform == 'darwin': extra_options = dict( diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 38c7d5b9..08c7773e 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -21,7 +21,15 @@ class addressGenerator(threading.Thread): queueValue = shared.addressGeneratorQueue.get() nonceTrialsPerByte = 0 payloadLengthExtraBytes = 0 - if len(queueValue) == 7: + if queueValue[0] == 'createChan': + command, addressVersionNumber, streamNumber, label, deterministicPassphrase = queueValue + eighteenByteRipe = False + numberOfAddressesToMake = 1 + elif queueValue[0] == 'joinChan': + command, chanAddress, label, deterministicPassphrase = queueValue + eighteenByteRipe = False + numberOfAddressesToMake = 1 + elif len(queueValue) == 7: command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe = queueValue elif len(queueValue) == 9: command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes = queueValue @@ -122,7 +130,7 @@ class addressGenerator(threading.Thread): shared.workerQueue.put(( 'doPOWForMyV3Pubkey', ripe.digest())) - elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress': + elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan': if len(deterministicPassphrase) == 0: sys.stderr.write( 'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.') @@ -176,7 +184,17 @@ class addressGenerator(threading.Thread): print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.' address = encodeAddress(3, streamNumber, ripe.digest()) - if command == 'createDeterministicAddresses': + saveAddressToDisk = True + # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address + if command == 'joinChan': + if address != chanAddress: + #todo: show an error message in the UI + shared.apiAddressGeneratorReturnQueue.put('API Error 0018: Chan name does not match address.') + saveAddressToDisk = False + if command == 'getDeterministicAddress': + saveAddressToDisk = False + + if saveAddressToDisk: # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. # https://en.bitcoin.it/wiki/Wallet_import_format privSigningKey = '\x80' + potentialPrivSigningKey @@ -198,6 +216,8 @@ class addressGenerator(threading.Thread): shared.config.set(address, 'label', label) shared.config.set(address, 'enabled', 'true') shared.config.set(address, 'decoy', 'false') + if command == 'joinChan' or command == 'createChan': + shared.config.set(address, 'chan', 'true') shared.config.set(address, 'noncetrialsperbyte', str( nonceTrialsPerByte)) shared.config.set(address, 'payloadlengthextrabytes', str( @@ -213,18 +233,11 @@ class addressGenerator(threading.Thread): label, address, str(streamNumber)))) listOfNewAddressesToSendOutThroughTheAPI.append( address) - # if eighteenByteRipe: - # shared.reloadMyAddressHashes()#This is - # necessary here (rather than just at the end) - # because otherwise if the human generates a - # large number of new addresses and uses one - # before they are done generating, the program - # will receive a getpubkey message and will - # ignore it. shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor( potentialPrivEncryptionKey.encode('hex')) shared.myAddressesByHash[ ripe.digest()] = address + #todo: don't send out pubkey if dealing with a chan; save in pubkeys table instead. shared.workerQueue.put(( 'doPOWForMyV3Pubkey', ripe.digest())) except: @@ -242,6 +255,7 @@ class addressGenerator(threading.Thread): # shared.reloadMyAddressHashes() elif command == 'getDeterministicAddress': shared.apiAddressGeneratorReturnQueue.put(address) + #todo: return things to the API if createChan or joinChan assuming saveAddressToDisk else: raise Exception( "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 5848e868..ebe6a7ff 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -69,6 +69,22 @@ class sqlThread(threading.Thread): 'ERROR trying to create database file (message.dat). Error message: %s\n' % str(err)) os._exit(0) + if shared.config.getint('bitmessagesettings', 'settingsversion') == 1: + shared.config.set('bitmessagesettings', 'settingsversion', '2') + # If the settings version is equal to 2 or 3 then the + # sqlThread will modify the pubkeys table and change + # the settings version to 4. + shared.config.set('bitmessagesettings', 'socksproxytype', 'none') + shared.config.set('bitmessagesettings', 'sockshostname', 'localhost') + shared.config.set('bitmessagesettings', 'socksport', '9050') + shared.config.set('bitmessagesettings', 'socksauthentication', 'false') + shared.config.set('bitmessagesettings', 'socksusername', '') + shared.config.set('bitmessagesettings', 'sockspassword', '') + shared.config.set('bitmessagesettings', 'keysencrypted', 'false') + shared.config.set('bitmessagesettings', 'messagesencrypted', 'false') + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + # People running earlier versions of PyBitmessage do not have the # usedpersonally field in their pubkeys table. Let's add it. if shared.config.getint('bitmessagesettings', 'settingsversion') == 2: @@ -158,7 +174,7 @@ class sqlThread(threading.Thread): self.cur.execute( ''' VACUUM ''') # After code refactoring, the possible status values for sent messages - # as changed. + # have changed. self.cur.execute( '''update sent set status='doingmsgpow' where status='doingpow' ''') self.cur.execute( diff --git a/src/helper_startup.py b/src/helper_startup.py index df27fd6e..e6590b0e 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -76,19 +76,3 @@ def loadConfig(): os.makedirs(shared.appdata) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) - - if shared.config.getint('bitmessagesettings', 'settingsversion') == 1: - shared.config.set('bitmessagesettings', 'settingsversion', '4') - # If the settings version is equal to 2 or 3 then the - # sqlThread will modify the pubkeys table and change - # the settings version to 4. - shared.config.set('bitmessagesettings', 'socksproxytype', 'none') - shared.config.set('bitmessagesettings', 'sockshostname', 'localhost') - shared.config.set('bitmessagesettings', 'socksport', '9050') - shared.config.set('bitmessagesettings', 'socksauthentication', 'false') - shared.config.set('bitmessagesettings', 'socksusername', '') - shared.config.set('bitmessagesettings', 'sockspassword', '') - shared.config.set('bitmessagesettings', 'keysencrypted', 'false') - shared.config.set('bitmessagesettings', 'messagesencrypted', 'false') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) diff --git a/src/shared.py b/src/shared.py index 22c3b9f6..4b3ef73b 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,4 +1,4 @@ -softwareVersion = '0.3.3-2' +softwareVersion = '0.3.4' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # Equals two days and 12 hours. lengthOfTimeToLeaveObjectsInInventory = 237600 # Equals two days and 18 hours. This should be longer than maximumAgeOfAnObjectThatIAmWillingToAccept so that we don't process messages twice. diff --git a/src/translations/bitmessage_fr_BE.pro b/src/translations/bitmessage_fr_BE.pro index 7b0a8814..d86fdc4d 100644 --- a/src/translations/bitmessage_fr_BE.pro +++ b/src/translations/bitmessage_fr_BE.pro @@ -1,21 +1,32 @@ -SOURCES = ../about.py\ - ../addresses.py\ - ../bitmessage_icons_rc.py\ +SOURCES = ../addresses.py\ ../bitmessagemain.py\ + ../class_addressGenerator.py\ + ../class_outgoingSynSender.py\ + ../class_receiveDataThread.py\ + ../class_sendDataThread.py\ + ../class_singleCleaner.py\ + ../class_singleListener.py\ + ../class_singleWorker.py\ + ../class_sqlThread.py\ + ../helper_bitcoin.py\ + ../helper_bootstrap.py\ + ../helper_generic.py\ + ../helper_inbox.py\ + ../helper_sent.py\ + ../helper_startup.py\ + ../shared.py ../bitmessageqt/__init__.py\ - ../bitmessageui.py\ - ../defaultKnownNodes.py\ - ../help.py\ - ../highlevelcrypto.py\ - ../iconglossary.py\ - ../newaddressdialog.py\ - ../newsubscriptiondialog.py\ - ../proofofwork.py\ - ../regenerateaddresses.py\ - ../settings.py\ - ../shared.py\ - ../singleton.py\ - ../specialaddressbehavior.py + ../bitmessageqt/about.py\ + ../bitmessageqt/bitmessageui.py\ + ../bitmessageqt/help.py\ + ../bitmessageqt/iconglossary.py\ + ../bitmessageqt/newaddressdialog.py\ + ../bitmessageqt/newchandialog.py\ + ../bitmessageqt/newsubscriptiondialog.py\ + ../bitmessageqt/regenerateaddresses.py\ + ../bitmessageqt/settings.py\ + ../bitmessageqt/specialaddressbehavior.py + TRANSLATIONS = bitmessage_fr_BE.ts