From 34824c044e4bd4b7774c0b8b78af6879270339b5 Mon Sep 17 00:00:00 2001 From: Yuri Date: Sun, 14 Sep 2014 23:34:33 -0700 Subject: [PATCH 1/4] Moved all keys.dat writing code into writeKeysFile in shared.py --- src/api.py | 6 ++--- src/bitmessagecurses/__init__.py | 15 ++++-------- src/bitmessageqt/__init__.py | 41 ++++++++++---------------------- src/class_addressGenerator.py | 6 ++--- src/class_singleWorker.py | 9 +++---- src/class_sqlThread.py | 21 ++++++---------- src/helper_startup.py | 3 +-- src/shared.py | 9 +++++++ 8 files changed, 41 insertions(+), 69 deletions(-) diff --git a/src/api.py b/src/api.py index bd28d923..42cae15d 100644 --- a/src/api.py +++ b/src/api.py @@ -429,8 +429,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if not shared.safeConfigGetBoolean(address, 'chan'): raise APIError(25, 'Specified address is not a chan address. Use deleteAddress API call instead.') shared.config.remove_section(address) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() return 'success' elif method == 'deleteAddress': @@ -443,8 +442,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if not shared.config.has_section(address): raise APIError(13, 'Could not find this address in your keys.dat file.') shared.config.remove_section(address) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() shared.UISignalQueue.put(('rerenderInboxFromLabels','')) shared.UISignalQueue.put(('rerenderSentToLabels','')) shared.reloadMyAddressHashes() diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py index 91c99df8..89fa6783 100644 --- a/src/bitmessagecurses/__init__.py +++ b/src/bitmessagecurses/__init__.py @@ -470,15 +470,13 @@ def handlech(c, stdscr): label = t shared.config.set(a, "label", label) # Write config - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() addresses[addrcur][0] = label elif t == "4": # Enable address a = addresses[addrcur][2] shared.config.set(a, "enabled", "true") # Set config # Write config - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Change color if shared.safeConfigGetBoolean(a, 'chan'): addresses[addrcur][3] = 9 # orange @@ -493,16 +491,14 @@ def handlech(c, stdscr): shared.config.set(a, "enabled", "false") # Set config addresses[addrcur][3] = 8 # Set color to gray # Write config - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() addresses[addrcur][1] = False shared.reloadMyAddressHashes() # Reload address hashes elif t == "6": # Delete address r, t = d.inputbox("Type in \"I want to delete this address\"", width=50) if r == d.DIALOG_OK and t == "I want to delete this address": shared.config.remove_section(addresses[addrcur][2]) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() del addresses[addrcur] elif t == "7": # Special address behavior a = addresses[addrcur][2] @@ -533,8 +529,7 @@ def handlech(c, stdscr): shared.config.set(a, "mailinglistname", mn) addresses[addrcur][3] = 6 # Set color to magenta # Write config - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() elif menutab == 5: d.set_background_title("Subscriptions Dialog Box") r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?", diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 47870046..6b46c9fa 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -462,8 +462,7 @@ class MyForm(QtGui.QMainWindow): self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: shared.config.remove_section(addressInKeysFile) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Configure Bitmessage to start on startup (or remove the # configuration) based on the setting in the keys.dat file @@ -1338,7 +1337,7 @@ class MyForm(QtGui.QMainWindow): reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Open keys.dat?"), _translate( "MainWindow", "You may manage your keys by editing the keys.dat file stored in\n %1 \nIt is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.)").arg(shared.appdata), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: - self.openKeysFile() + shared.openKeysFile() def click_actionDeleteAllTrashedMessages(self): if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: @@ -1428,17 +1427,10 @@ class MyForm(QtGui.QMainWindow): if self.connectDialogInstance.exec_(): if self.connectDialogInstance.ui.radioButtonConnectNow.isChecked(): shared.config.remove_option('bitmessagesettings', 'dontconnect') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() else: self.click_actionSettings() - def openKeysFile(self): - if 'linux' in sys.platform: - subprocess.call(["xdg-open", shared.appdata + 'keys.dat']) - else: - os.startfile(shared.appdata + 'keys.dat') - def changeEvent(self, event): if event.type() == QtCore.QEvent.WindowStateChange: if self.windowState() & QtCore.Qt.WindowMinimized: @@ -2385,8 +2377,7 @@ class MyForm(QtGui.QMainWindow): # shared.config.set('bitmessagesettings', 'maxcores', # str(self.settingsDialogInstance.ui.comboBoxMaxCores.currentText())) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() if 'win32' in sys.platform or 'win64' in sys.platform: # Auto-startup for Windows @@ -2406,8 +2397,7 @@ class MyForm(QtGui.QMainWindow): if shared.appdata != '' and self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we are NOT using portable mode now but the user selected that we should... # Write the keys.dat file to disk in the new location sqlStoredProcedure('movemessagstoprog') - with open('keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Write the knownnodes.dat file to disk in the new location shared.knownNodesLock.acquire() output = open('knownnodes.dat', 'wb') @@ -2431,8 +2421,7 @@ class MyForm(QtGui.QMainWindow): os.makedirs(shared.appdata) sqlStoredProcedure('movemessagstoappdata') # Write the keys.dat file to disk in the new location - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Write the knownnodes.dat file to disk in the new location shared.knownNodesLock.acquire() output = open(shared.appdata + 'knownnodes.dat', 'wb') @@ -2451,8 +2440,7 @@ class MyForm(QtGui.QMainWindow): def click_radioButtonBlacklist(self): if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white': shared.config.set('bitmessagesettings', 'blackwhitelist', 'black') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # self.ui.tableWidgetBlacklist.clearContents() self.ui.tableWidgetBlacklist.setRowCount(0) self.loadBlackWhiteList() @@ -2461,8 +2449,7 @@ class MyForm(QtGui.QMainWindow): def click_radioButtonWhitelist(self): if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': shared.config.set('bitmessagesettings', 'blackwhitelist', 'white') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # self.ui.tableWidgetBlacklist.clearContents() self.ui.tableWidgetBlacklist.setRowCount(0) self.loadBlackWhiteList() @@ -2534,8 +2521,7 @@ class MyForm(QtGui.QMainWindow): shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( self.dialog.ui.lineEditMailingListName.text().toUtf8())) self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() self.rerenderInboxToLabels() def click_NewAddressDialog(self): @@ -3037,8 +3023,7 @@ class MyForm(QtGui.QMainWindow): addressAtCurrentRow = str( self.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) shared.config.set(addressAtCurrentRow, 'enabled', 'true') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() self.ui.tableWidgetYourIdentities.item( currentRow, 0).setTextColor(QApplication.palette().text().color()) self.ui.tableWidgetYourIdentities.item( @@ -3064,8 +3049,7 @@ class MyForm(QtGui.QMainWindow): currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128)) if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() shared.reloadMyAddressHashes() def on_action_YourIdentitiesClipboard(self): @@ -3262,8 +3246,7 @@ class MyForm(QtGui.QMainWindow): currentRow, 1).text() shared.config.set(str(addressAtCurrentRow), 'label', str( self.ui.tableWidgetYourIdentities.item(currentRow, 0).text().toUtf8())) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() self.rerenderComboBoxSendFrom() # self.rerenderInboxFromLabels() self.rerenderInboxToLabels() diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 2e215606..1a3ff5bc 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -129,8 +129,7 @@ class addressGenerator(threading.Thread): address, 'privSigningKey', privSigningKeyWIF) shared.config.set( address, 'privEncryptionKey', privEncryptionKeyWIF) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # The API and the join and create Chan functionality # both need information back from the address generator. @@ -244,8 +243,7 @@ class addressGenerator(threading.Thread): address, 'privSigningKey', privSigningKeyWIF) shared.config.set( address, 'privEncryptionKey', privEncryptionKeyWIF) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() shared.UISignalQueue.put(('writeNewAddressToTable', ( label, address, str(streamNumber)))) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 5e8f11e8..1d590e63 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -150,8 +150,7 @@ class singleWorker(threading.Thread): try: shared.config.set( myAddress, 'lastpubkeysendtime', str(int(time.time()))) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() except: # The user deleted the address out of the keys.dat file before this # finished. @@ -236,8 +235,7 @@ class singleWorker(threading.Thread): try: shared.config.set( myAddress, 'lastpubkeysendtime', str(int(time.time()))) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() except: # The user deleted the address out of the keys.dat file before this # finished. @@ -332,8 +330,7 @@ class singleWorker(threading.Thread): try: shared.config.set( myAddress, 'lastpubkeysendtime', str(int(time.time()))) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() except Exception as err: logger.error('Error: Couldn\'t add the lastpubkeysendtime to the keys.dat file. Error message: %s' % err) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 2df6c05f..fdc793d1 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -86,8 +86,7 @@ class sqlThread(threading.Thread): shared.config.set('bitmessagesettings', 'sockslisten', 'false') 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) + shared.writeKeysFile() # People running earlier versions of PyBitmessage do not have the # usedpersonally field in their pubkeys table. Let's add it. @@ -98,8 +97,7 @@ class sqlThread(threading.Thread): self.conn.commit() shared.config.set('bitmessagesettings', 'settingsversion', '3') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # People running earlier versions of PyBitmessage do not have the # encodingtype field in their inbox and sent tables or the read field @@ -119,8 +117,7 @@ class sqlThread(threading.Thread): self.conn.commit() shared.config.set('bitmessagesettings', 'settingsversion', '4') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() if shared.config.getint('bitmessagesettings', 'settingsversion') == 4: shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str( @@ -135,8 +132,7 @@ class sqlThread(threading.Thread): shared.config.set( 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'settingsversion', '6') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # From now on, let us keep a 'version' embedded in the messages.dat # file so that when we make changes to the database, the database @@ -251,8 +247,7 @@ class sqlThread(threading.Thread): if int(shared.config.get('bitmessagesettings','defaultnoncetrialsperbyte')) == shared.networkDefaultProofOfWorkNonceTrialsPerByte: shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte * 2)) shared.config.set('bitmessagesettings', 'settingsversion', '7') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Add a new column to the pubkeys table to store the address version. # We're going to trash all of our pubkeys and let them be redownloaded. @@ -275,8 +270,7 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): # acts as a salt shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons # Since we've added a new config entry, let's write keys.dat to disk. - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() #Adjusting time period to stop sending messages if shared.config.getint('bitmessagesettings', 'settingsversion') == 7: @@ -286,8 +280,7 @@ class sqlThread(threading.Thread): 'bitmessagesettings', 'stopresendingafterxmonths', '') #shared.config.set( shared.config.set('bitmessagesettings', 'settingsversion', '8') - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() # Add a new table: objectprocessorqueue with which to hold objects # that have yet to be processed if the user shuts down Bitmessage. diff --git a/src/helper_startup.py b/src/helper_startup.py index fd14e528..0e69865e 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -131,8 +131,7 @@ def loadConfig(): os.makedirs(shared.appdata) if not sys.platform.startswith('win'): os.umask(0o077) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + shared.writeKeysFile() _loadTrustedPeer() diff --git a/src/shared.py b/src/shared.py index 3f60a852..14a82f41 100644 --- a/src/shared.py +++ b/src/shared.py @@ -792,6 +792,15 @@ def checkAndShareBroadcastWithPeers(data): shared.objectProcessorQueueSize += len(data) objectProcessorQueue.put((objectType,data)) +def openKeysFile(): + if 'linux' in sys.platform: + subprocess.call(["xdg-open", shared.appdata + 'keys.dat']) + else: + os.startfile(shared.appdata + 'keys.dat') + +def writeKeysFile(): + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) helper_startup.loadConfig() from debug import logger From d3c91eea3bb960bcc85a1875d95fad3465265177 Mon Sep 17 00:00:00 2001 From: Yuri Date: Sun, 14 Sep 2014 23:53:21 -0700 Subject: [PATCH 2/4] Added backup copy creation during keys.dat write to prevent an accidental file loss due to the disk failure. --- src/shared.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 14a82f41..32e4b118 100644 --- a/src/shared.py +++ b/src/shared.py @@ -20,6 +20,7 @@ import sys import stat import threading import time +import shutil # used for moving the data folder and copying keys.dat from os import path, environ from struct import Struct @@ -799,8 +800,14 @@ def openKeysFile(): os.startfile(shared.appdata + 'keys.dat') def writeKeysFile(): - with open(shared.appdata + 'keys.dat', 'wb') as configfile: + fileName = shared.appdata + 'keys.dat' + # create a backup copy to prevent the accidental loss due to the disk write failure + shutil.copyfile(fileName, fileName + '.bak') + # write the file + with open(fileName, 'wb') as configfile: shared.config.write(configfile) + # delete a backup + os.remove(fileName + '.bak') helper_startup.loadConfig() from debug import logger From 5beaeff2e2dec032468f34464eb7a0b329efa41f Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 16 Sep 2014 10:04:56 -0700 Subject: [PATCH 3/4] TImestamped the keys.dat backup file. --- src/shared.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared.py b/src/shared.py index 32e4b118..37a5cca7 100644 --- a/src/shared.py +++ b/src/shared.py @@ -21,6 +21,7 @@ import stat import threading import time import shutil # used for moving the data folder and copying keys.dat +import datetime from os import path, environ from struct import Struct @@ -801,13 +802,14 @@ def openKeysFile(): def writeKeysFile(): fileName = shared.appdata + 'keys.dat' + fileNameBak = fileName + "." + datetime.datetime.now().strftime("%Y%j%H%M%S%f") + '.bak' # create a backup copy to prevent the accidental loss due to the disk write failure - shutil.copyfile(fileName, fileName + '.bak') + shutil.copyfile(fileName, fileNameBak) # write the file with open(fileName, 'wb') as configfile: shared.config.write(configfile) # delete a backup - os.remove(fileName + '.bak') + os.remove(fileNameBak) helper_startup.loadConfig() from debug import logger From 9b6bc26144395e05bdc3ae06098a92260f2b48b8 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 25 Dec 2014 21:06:10 -0500 Subject: [PATCH 4/4] fix bug in #715 --- src/bitmessageqt/__init__.py | 3 ++- src/shared.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 95eb7d12..d3bf8dae 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2426,7 +2426,8 @@ class MyForm(QtGui.QMainWindow): if shared.appdata != '' and self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we are NOT using portable mode now but the user selected that we should... # Write the keys.dat file to disk in the new location sqlStoredProcedure('movemessagstoprog') - shared.writeKeysFile() + with open('keys.dat', 'wb') as configfile: + shared.config.write(configfile) # Write the knownnodes.dat file to disk in the new location shared.knownNodesLock.acquire() output = open('knownnodes.dat', 'wb') diff --git a/src/shared.py b/src/shared.py index df37c1b4..8f4be2ff 100644 --- a/src/shared.py +++ b/src/shared.py @@ -850,12 +850,17 @@ def writeKeysFile(): fileName = shared.appdata + 'keys.dat' fileNameBak = fileName + "." + datetime.datetime.now().strftime("%Y%j%H%M%S%f") + '.bak' # create a backup copy to prevent the accidental loss due to the disk write failure - shutil.copyfile(fileName, fileNameBak) + try: + shutil.copyfile(fileName, fileNameBak) + # The backup succeeded. This can fail if the file didn't exist before. + existingFileNameExisted = True + except: + existingFileNameExisted = False # write the file with open(fileName, 'wb') as configfile: shared.config.write(configfile) # delete a backup - os.remove(fileNameBak) + if existingFileNameExisted: + os.remove(fileNameBak) -helper_startup.loadConfig() from debug import logger