From 807f2fee8a51d9823628c30deff05124599ace6b Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 4 Aug 2014 18:35:26 -0400 Subject: [PATCH 01/12] increment version number to 0.4.3 --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 3f60a852..c82bf73a 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,4 +1,4 @@ -softwareVersion = '0.4.2' +softwareVersion = '0.4.3' 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. -- 2.45.1 From 269506ff8ff4f0afdfb0a148097bd4bc11197419 Mon Sep 17 00:00:00 2001 From: bmng-dev Date: Wed, 6 Aug 2014 02:01:01 +0000 Subject: [PATCH 02/12] New localization module (l10n) Resolves #691 --- src/bitmessagecurses/__init__.py | 18 +++--- src/bitmessageqt/__init__.py | 99 ++++++++------------------------ src/class_objectProcessor.py | 4 +- src/class_singleWorker.py | 21 +++---- src/l10n.py | 42 ++++++++++++++ 5 files changed, 86 insertions(+), 98 deletions(-) create mode 100644 src/l10n.py diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py index 91c99df8..1c88d222 100644 --- a/src/bitmessagecurses/__init__.py +++ b/src/bitmessagecurses/__init__.py @@ -25,6 +25,7 @@ import shared import ConfigParser from addresses import * from pyelliptic.openssl import OpenSSL +import l10n quit = False menutab = 1 @@ -210,7 +211,7 @@ def drawtab(stdscr): stdscr.addstr(8+i, 18, str(item).ljust(2)) # Uptime and processing data - stdscr.addstr(6, 35, "Since startup on "+unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(startuptime))))) + stdscr.addstr(6, 35, "Since startup on "+l10n.formatTimestamp(startuptime, False)) stdscr.addstr(7, 40, "Processed "+str(shared.numberOfMessagesProcessed).ljust(4)+" person-to-person messages.") stdscr.addstr(8, 40, "Processed "+str(shared.numberOfBroadcastsProcessed).ljust(4)+" broadcast messages.") stdscr.addstr(9, 40, "Processed "+str(shared.numberOfPubkeysProcessed).ljust(4)+" public keys.") @@ -851,8 +852,7 @@ def loadInbox(): # Load into array inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject, - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(received))), - read]) + l10n.formatTimestamp(received, False), read]) inbox.reverse() def loadSent(): sys.stdout = sys.__stdout__ @@ -902,20 +902,20 @@ def loadSent(): elif status == "msgqueued": statstr = "Message queued" elif status == "msgsent": - t = strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime))) + t = l10n.formatTimestamp(lastactiontime, False) statstr = "Message sent at "+t+".Waiting for acknowledgement." elif status == "msgsentnoackexpected": - t = strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime))) + t = l10n.formatTimestamp(lastactiontime, False) statstr = "Message sent at "+t+"." elif status == "doingmsgpow": statstr = "The proof of work required to send the message has been queued." elif status == "askreceived": - t = strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime))) + t = l10n.formatTimestamp(lastactiontime, False) statstr = "Acknowledgment of the message received at "+t+"." elif status == "broadcastqueued": statstr = "Broadcast queued." elif status == "broadcastsent": - t = strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime))) + t = l10n.formatTimestamp(lastactiontime, False) statstr = "Broadcast sent at "+t+"." elif status == "forcepow": statstr = "Forced difficulty override. Message will start sending soon." @@ -924,12 +924,12 @@ def loadSent(): elif status == "toodifficult": statstr = "Error: The work demanded by the recipient is more difficult than you are willing to do." else: - t = strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime))) + t = l10n.formatTimestamp(lastactiontime, False) statstr = "Unknown status "+status+" at "+t+"." # Load into array sentbox.append([tolabel, toaddr, fromlabel, fromaddr, subject, statstr, ackdata, - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(lastactiontime)))]) + l10n.formatTimestamp(lastactiontime, False)]) sentbox.reverse() def loadAddrBook(): sys.stdout = sys.__stdout__ diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 47870046..e00011a9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1,8 +1,3 @@ -try: - import locale -except: - pass - withMessagingMenu = False try: from gi.repository import MessagingMenu @@ -40,6 +35,7 @@ from debug import logger import subprocess import datetime from helper_sql import * +import l10n try: from PyQt4 import QtCore, QtGui @@ -578,7 +574,7 @@ class MyForm(QtGui.QMainWindow): self.statusbar = self.statusBar() self.statusbar.insertPermanentWidget(0, self.ui.pushButtonStatusIcon) self.ui.labelStartupTime.setText(_translate("MainWindow", "Since startup on %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))),'utf-8'))) + l10n.formatTimestamp())) self.numberOfMessagesProcessed = 0 self.numberOfBroadcastsProcessed = 0 self.numberOfPubkeysProcessed = 0 @@ -837,34 +833,34 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Queued.") elif status == 'msgsent': statusText = _translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent at %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + l10n.formatTimestamp(lastactiontime)) elif status == 'msgsentnoackexpected': statusText = _translate("MainWindow", "Message sent. Sent at %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + l10n.formatTimestamp(lastactiontime)) elif status == 'doingmsgpow': statusText = _translate( "MainWindow", "Need to do work to send message. Work is queued.") elif status == 'ackreceived': statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + l10n.formatTimestamp(lastactiontime)) elif status == 'broadcastqueued': statusText = _translate( "MainWindow", "Broadcast queued.") elif status == 'broadcastsent': - statusText = _translate("MainWindow", "Broadcast on %1").arg(unicode(strftime( - shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + statusText = _translate("MainWindow", "Broadcast on %1").arg( + l10n.formatTimestamp(lastactiontime)) elif status == 'toodifficult': statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + l10n.formatTimestamp(lastactiontime)) elif status == 'badkey': statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg( - unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + l10n.formatTimestamp(lastactiontime)) elif status == 'forcepow': statusText = _translate( "MainWindow", "Forced difficulty override. Send should start soon.") else: - statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) + statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg( + l10n.formatTimestamp(lastactiontime)) newItem = myTableWidgetItem(statusText) newItem.setToolTip(statusText) newItem.setData(Qt.UserRole, QByteArray(ackdata)) @@ -971,10 +967,8 @@ class MyForm(QtGui.QMainWindow): subject_item.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, subject_item) # time received - time_item = myTableWidgetItem(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(received))), 'utf-8')) - time_item.setToolTip(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(received))), 'utf-8')) + time_item = myTableWidgetItem(l10n.formatTimestamp(received)) + time_item.setToolTip(l10n.formatTimestamp(received)) time_item.setData(Qt.UserRole, QByteArray(msgid)) time_item.setData(33, int(received)) time_item.setFlags( @@ -2062,12 +2056,9 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send # broadcast...'+ - # unicode(strftime(shared.config.get('bitmessagesettings', - # 'timeformat'),localtime(int(time.time()))),'utf-8')) - newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))) - newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))) + # l10n.formatTimestamp()) + newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) + newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) newItem.setData(Qt.UserRole, QByteArray(ackdata)) newItem.setData(33, int(time.time())) self.ui.tableWidgetSent.setItem(0, 3, newItem) @@ -2134,10 +2125,8 @@ class MyForm(QtGui.QMainWindow): #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) - newItem = myTableWidgetItem(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')) - newItem.setToolTip(unicode(strftime(shared.config.get( - 'bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')) + newItem = myTableWidgetItem(l10n.formatTimestamp()) + newItem.setToolTip(l10n.formatTimestamp()) newItem.setData(Qt.UserRole, QByteArray(inventoryHash)) newItem.setData(33, int(time.time())) newItem.setFont(font) @@ -3811,52 +3800,12 @@ def run(): app = QtGui.QApplication(sys.argv) translator = QtCore.QTranslator() - try: - locale_countrycode = str(locale.getdefaultlocale()[0]) - except: - # The above is not compatible with all versions of OSX. - locale_countrycode = "en_US" # Default to english. - locale_lang = locale_countrycode[0:2] - user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) - user_lang = user_countrycode[0:2] - try: - translation_path = os.path.join(sys._MEIPASS, "translations/bitmessage_") - except Exception, e: - translation_path = "translations/bitmessage_" - - if shared.config.get('bitmessagesettings', 'userlocale') == 'system': - # try to detect the users locale otherwise fallback to English - try: - # try the users full locale, e.g. 'en_US': - # since we usually only provide languages, not localozations - # this will usually fail - translator.load(translation_path + locale_countrycode) - except: - try: - # try the users locale language, e.g. 'en': - # since we usually only provide languages, not localozations - # this will usually succeed - translator.load(translation_path + locale_lang) - except: - # as English is already the default language, we don't - # need to do anything. No need to translate. - pass - else: - try: - # check if the user input is a valid translation file: - # since user_countrycode will be usually set by the combobox - # it will usually just be a language code - translator.load(translation_path + user_countrycode) - except: - try: - # check if the user lang is a valid translation file: - # this is only needed if the user manually set his 'userlocale' - # in the keys.dat to a countrycode (e.g. 'de_CH') - translator.load(translation_path + user_lang) - except: - # as English is already the default language, we don't - # need to do anything. No need to translate. - pass + translationpath = os.path.join( + getattr(sys, '_MEIPASS', ''), + 'translations', + 'bitmessage_' + l10n.getTranslationLanguage() + ) + translator.load(translationpath) QtGui.QApplication.installTranslator(translator) app.setStyleSheet("QStatusBar::item { border: 0px solid black }") diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index b8a3c949..326ba98a 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -19,6 +19,7 @@ import helper_sent from helper_sql import * import tr from debug import logger +import l10n class objectProcessor(threading.Thread): @@ -421,8 +422,7 @@ class objectProcessor(threading.Thread): del shared.ackdataForWhichImWatching[data[readPosition:]] sqlExecute('UPDATE sent SET status=? WHERE ackdata=?', 'ackreceived', data[readPosition:]) - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(unicode( - time.strftime(shared.config.get('bitmessagesettings', 'timeformat'), time.localtime(int(time.time()))), 'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(l10n.formatTimestamp())))) return else: logger.info('This was NOT an acknowledgement bound for me.') diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 5e8f11e8..e5353bda 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -13,6 +13,7 @@ from debug import logger from helper_sql import * import helper_inbox from helper_generic import addDataPadding +import l10n # This thread, of which there is only one, does the heavy lifting: # calculating POWs. @@ -440,8 +441,7 @@ class singleWorker(threading.Thread): shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(l10n.formatTimestamp())))) # Update the status of the message in the 'sent' table to have # a 'broadcastsent' status @@ -616,7 +616,7 @@ class singleWorker(threading.Thread): 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'))))) + 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(l10n.formatTimestamp())))) # 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 @@ -655,7 +655,7 @@ class singleWorker(threading.Thread): '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''', ackdata) shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float( - requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(l10n.formatTimestamp())))) continue else: # if we are sending a message to ourselves or a chan.. with shared.printLock: @@ -666,7 +666,7 @@ class singleWorker(threading.Thread): privEncryptionKeyBase58 = shared.config.get( toaddress, 'privencryptionkey') except Exception as err: - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(l10n.formatTimestamp())))) with shared.printLock: sys.stderr.write( 'Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err) @@ -803,7 +803,7 @@ class singleWorker(threading.Thread): encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) except: sqlExecute('''UPDATE sent SET status='badkey' WHERE ackdata=?''', ackdata) - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(l10n.formatTimestamp())))) continue encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) @@ -828,12 +828,10 @@ class singleWorker(threading.Thread): objectType, toStreamNumber, encryptedPayload, int(time.time()),'') shared.inventorySets[toStreamNumber].add(inventoryHash) if shared.config.has_section(toaddress): - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(l10n.formatTimestamp())))) else: # not sending to a chan or one of my addresses - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( toStreamNumber, 'advertiseobject', inventoryHash)) @@ -926,8 +924,7 @@ class singleWorker(threading.Thread): shared.UISignalQueue.put(( 'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.'))) - shared.UISignalQueue.put(('updateSentItemStatusByHash', (ripe, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + shared.UISignalQueue.put(('updateSentItemStatusByHash', (ripe, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(l10n.formatTimestamp())))) def generateFullAckMessage(self, ackdata, toStreamNumber): embeddedTime = pack('>Q', (int(time.time()) + random.randrange( diff --git a/src/l10n.py b/src/l10n.py new file mode 100644 index 00000000..c04b5cdb --- /dev/null +++ b/src/l10n.py @@ -0,0 +1,42 @@ + +import logging +import time + +import shared + + +#logger = logging.getLogger(__name__) +logger = logging.getLogger('file_only') + +try: + import locale + encoding = locale.getpreferredencoding(False) + language = locale.getlocale()[0] or locale.getdefaultlocale()[0] +except: + logger.exception('Could not determine language or encoding') + if not encoding: + encoding = 'ISO8859-1' + language = 'en_US' + + +time_format = shared.config.get('bitmessagesettings', 'timeformat') + +def formatTimestamp(timestamp = None, as_unicode = True): + if timestamp and isinstance(timestamp, (float, int)): + timestring = time.strftime(time_format, time.localtime(timestamp)) + else: + timestring = time.strftime(time_format) + if as_unicode: + return unicode(timestring, encoding) + return timestring + +def getTranslationLanguage(): + userlocale = None + if shared.config.has_option('bitmessagesettings', 'userlocale'): + userlocale = shared.config.get('bitmessagesettings', 'userlocale') + + if userlocale in [None, '', 'system']: + return language + + return userlocale + -- 2.45.1 From ae01199b43b11abcd4f2aeedc93298318ff6964f Mon Sep 17 00:00:00 2001 From: benhc123 Date: Wed, 6 Aug 2014 20:08:37 +0100 Subject: [PATCH 03/12] Made references capitalised For neatness. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 73b5329c..7a161d04 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ pseudo-mailing list: BM-2D9QKN4teYRvoq2fyzpiftPh9WP9qggtzh -references +References ---------- -* [project website](https://bitmessage.org) -* [protocol specification](https://bitmessage.org/wiki/Protocol_specification) -* [whitepaper](https://bitmessage.org/bitmessage.pdf) -* [installation](https://bitmessage.org/wiki/Compiling_instructions) +* [Project Website](https://bitmessage.org) +* [Protocol Specification](https://bitmessage.org/wiki/Protocol_specification) +* [Whitepaper](https://bitmessage.org/bitmessage.pdf) +* [Installation](https://bitmessage.org/wiki/Compiling_instructions) -- 2.45.1 From ea6f34ade6d9035648bf29bbb29b6edaf6a14e48 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 6 Aug 2014 15:54:59 -0400 Subject: [PATCH 04/12] removed use of memoryview so that we can support python 2.7.3 --- src/bitmessagemain.py | 6 +-- src/class_receiveDataThread.py | 69 ++++++++++-------------- src/class_sendDataThread.py | 99 ++++++++++++++++------------------ src/shared.py | 2 +- 4 files changed, 80 insertions(+), 96 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 83e729c5..491b82f0 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -19,12 +19,12 @@ import sys #potentially render this version check useless. So logging won't be used here until #there is a more efficient way to configure logging if sys.hexversion >= 0x3000000: - msg = "PyBitmessage does not support Python 3. Python 2.7.5 or later is required. Your version: %s" % sys.version + msg = "PyBitmessage does not support Python 3. Python 2.7.3 or later is required. Your version: %s" % sys.version #logger.critical(msg) sys.stdout.write(msg) sys.exit(0) -if sys.hexversion < 0x20705F0: - msg = "You should use Python 2.7.5 or greater (but not Python 3). Your version: %s" % sys.version +if sys.hexversion < 0x20703F0: + msg = "You should use Python 2.7.3 or greater (but not Python 3). Your version: %s" % sys.version #logger.critical(msg) sys.stdout.write(msg) sys.exit(0) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index a97da9df..879faff6 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -62,7 +62,7 @@ class receiveDataThread(threading.Thread): def run(self): with shared.printLock: - print 'ID of the receiveDataThread is', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList) + print 'receiveDataThread starting. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList) while True: dataLen = len(self.data) @@ -76,7 +76,7 @@ class receiveDataThread(threading.Thread): break except Exception as err: with shared.printLock: - print 'sock.recv error. Closing receiveData thread (HOST:', self.peer, 'ID:', str(id(self)) + ').', err + print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', err break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: @@ -92,7 +92,7 @@ class receiveDataThread(threading.Thread): print 'removed self (a receiveDataThread) from selfInitiatedConnections' except: pass - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) # commands the corresponding sendDataThread to shut itself down. + self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down. try: del shared.connectedHostsList[self.peer.host] except Exception as err: @@ -106,47 +106,33 @@ class receiveDataThread(threading.Thread): pass shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) with shared.printLock: - print 'The size of the connectedHostsList is now:', len(shared.connectedHostsList) + print 'receiveDataThread ending. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList) def processData(self): - # if shared.verbose >= 3: - # with shared.printLock: - # print 'self.data is currently ', repr(self.data) - # if len(self.data) < shared.Header.size: # if so little of the data has arrived that we can't even read the checksum then wait for more data. return - #Use a memoryview so we don't copy data unnecessarily - view = memoryview(self.data) - magic,command,payloadLength,checksum = shared.Header.unpack(view[:shared.Header.size]) - view = view[shared.Header.size:] + + magic,command,payloadLength,checksum = shared.Header.unpack(self.data[:shared.Header.size]) if magic != 0xE9BEB4D9: - #if shared.verbose >= 1: - # with shared.printLock: - # print 'The magic bytes were not correct. First 40 bytes of data: ' + repr(self.data[0:40]) - self.data = "" return if payloadLength > 20000000: logger.info('The incoming message, which we have not yet download, is too large. Ignoring it. (unfortunately there is no way to tell the other node to stop sending it except to disconnect.) Message size: %s' % payloadLength) - self.data = view[payloadLength:].tobytes() - del view,magic,command,payloadLength,checksum # we don't need these anymore and better to clean them now before the recursive call rather than after + self.data = self.data[payloadLength + shared.Header.size:] + del magic,command,payloadLength,checksum # we don't need these anymore and better to clean them now before the recursive call rather than after self.processData() return - if len(view) < payloadLength: # check if the whole message has arrived yet. + if len(self.data) < payloadLength + shared.Header.size: # check if the whole message has arrived yet. return - payload = view[:payloadLength] + payload = self.data[shared.Header.size:payloadLength + shared.Header.size] if checksum != hashlib.sha512(payload).digest()[0:4]: # test the checksum in the message. print 'Checksum incorrect. Clearing this message.' - self.data = view[payloadLength:].tobytes() - del view,magic,command,payloadLength,checksum,payload #again better to clean up before the recursive call + self.data = self.data[payloadLength + shared.Header.size:] + del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call self.processData() return - - #We can now revert back to bytestrings and take this message out - payload = payload.tobytes() - self.data = view[payloadLength:].tobytes() - del view,magic,payloadLength,checksum + # The time we've last seen this node is obviously right now since we # just received valid data from it. So update the knownNodes list so # that other peers can be made aware of its existance. @@ -187,8 +173,11 @@ class receiveDataThread(threading.Thread): # pass #elif command == 'alert': # pass + + del payload + self.data = self.data[payloadLength + shared.Header.size:] # take this message out and then process the next message - if self.data == '': + if self.data == '': # if there are no more messages while len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: shared.numberOfInventoryLookupsPerformed += 1 objectHash, = random.sample( @@ -196,24 +185,22 @@ class receiveDataThread(threading.Thread): if objectHash in shared.inventory: with shared.printLock: print 'Inventory (in memory) already has object listed in inv message.' - del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] elif shared.isInSqlInventory(objectHash): if shared.verbose >= 3: with shared.printLock: print 'Inventory (SQL on disk) already has object listed in inv message.' - del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] else: + # We don't have the object in our inventory. Let's request it. self.sendgetdata(objectHash) del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] # It is possible that the remote node might not respond with the object. In that case, we'll very likely get it from someone else anyway. if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) - + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0' try: del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. @@ -221,9 +208,9 @@ class receiveDataThread(threading.Thread): pass break if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: + # We had objectsThatWeHaveYetToGetFromThisPeer but the loop ran, they were all in our inventory, and now we don't have any to get anymore. with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) - + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0' try: del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. @@ -239,12 +226,14 @@ class receiveDataThread(threading.Thread): def sendpong(self): - print 'Sending pong' + with shared.printLock: + print 'Sending pong' self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('pong'))) def recverack(self): - print 'verack received' + with shared.printLock: + print 'verack received' self.verackReceived = True if self.verackSent: # We have thus both sent and received a verack. @@ -279,7 +268,7 @@ class receiveDataThread(threading.Thread): with shared.printLock: print 'We are connected to too many people. Closing connection.' - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) + self.sendDataThreadQueue.put((0, 'shutdown','no data')) return self.sendBigInv() @@ -742,7 +731,7 @@ class receiveDataThread(threading.Thread): return self.remoteProtocolVersion, = unpack('>L', data[:4]) if self.remoteProtocolVersion <= 1: - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) + self.sendDataThreadQueue.put((0, 'shutdown','no data')) with shared.printLock: print 'Closing connection to old protocol version 1 node: ', self.peer return @@ -765,7 +754,7 @@ class receiveDataThread(threading.Thread): print 'Remote node useragent:', useragent, ' stream number:', self.streamNumber if self.streamNumber != 1: - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) + self.sendDataThreadQueue.put((0, 'shutdown','no data')) with shared.printLock: print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.' return @@ -776,7 +765,7 @@ class receiveDataThread(threading.Thread): if not self.initiatedConnection: self.sendDataThreadQueue.put((0, 'setStreamNumber', self.streamNumber)) if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) + self.sendDataThreadQueue.put((0, 'shutdown','no data')) with shared.printLock: print 'Closing connection to myself: ', self.peer return diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index eec23e7b..1d8c87b8 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -20,9 +20,6 @@ class sendDataThread(threading.Thread): threading.Thread.__init__(self) self.sendDataThreadQueue = sendDataThreadQueue shared.sendDataQueues.append(self.sendDataThreadQueue) - with shared.printLock: - print 'The length of sendDataQueues at sendDataThread init is:', len(shared.sendDataQueues) - self.data = '' self.objectHashHolderInstance = objectHashHolder(self.sendDataThreadQueue) self.objectHashHolderInstance.start() @@ -67,17 +64,20 @@ class sendDataThread(threading.Thread): def sendBytes(self, data): self.sock.sendall(data) shared.numberOfBytesSent += len(data) + self.lastTimeISentData = int(time.time()) + def run(self): + with shared.printLock: + print 'sendDataThread starting. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues) while True: deststream, command, data = self.sendDataThreadQueue.get() if deststream == self.streamNumber or deststream == 0: if command == 'shutdown': - if data == self.peer or data == 'all': - with shared.printLock: - print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.' - break + with shared.printLock: + print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.' + break # When you receive an incoming connection, a sendDataThread is # created even though you don't yet know what stream number the # remote peer is interested in. They will tell you in a version @@ -96,49 +96,44 @@ class sendDataThread(threading.Thread): elif command == 'advertisepeer': self.objectHashHolderInstance.holdPeer(data) elif command == 'sendaddr': - if not self.connectionIsOrWasFullyEstablished: - # not sending addr because we haven't sent and heard a verack from the remote node yet - return - numberOfAddressesInAddrMessage = len( - data) - payload = '' - for hostDetails in data: - timeLastReceivedMessageFromThisNode, streamNumber, services, host, port = hostDetails - payload += pack( - '>Q', timeLastReceivedMessageFromThisNode) # now uses 64-bit time - payload += pack('>I', streamNumber) - payload += pack( - '>q', services) # service bit flags offered by this node - payload += shared.encodeHost(host) - payload += pack('>H', port) - - payload = encodeVarint(numberOfAddressesInAddrMessage) + payload - packet = shared.CreatePacket('addr', payload) - try: - self.sendBytes(packet) - self.lastTimeISentData = int(time.time()) - except: - print 'sendaddr: self.sock.sendall failed' - break + if self.connectionIsOrWasFullyEstablished: # only send addr messages if we have send and heard a verack from the remote node + numberOfAddressesInAddrMessage = len(data) + payload = '' + for hostDetails in data: + timeLastReceivedMessageFromThisNode, streamNumber, services, host, port = hostDetails + payload += pack( + '>Q', timeLastReceivedMessageFromThisNode) # now uses 64-bit time + payload += pack('>I', streamNumber) + payload += pack( + '>q', services) # service bit flags offered by this node + payload += shared.encodeHost(host) + payload += pack('>H', port) + + payload = encodeVarint(numberOfAddressesInAddrMessage) + payload + packet = shared.CreatePacket('addr', payload) + try: + self.sendBytes(packet) + except: + with shared.printLock: + print 'sendaddr: self.sock.sendall failed' + break elif command == 'advertiseobject': self.objectHashHolderInstance.holdHash(data) elif command == 'sendinv': - if not self.connectionIsOrWasFullyEstablished: - # not sending inv because we haven't sent and heard a verack from the remote node yet - return - payload = '' - for hash in data: - if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: - payload += hash - if payload != '': - payload = encodeVarint(len(payload)/32) + payload - packet = shared.CreatePacket('inv', payload) - try: - self.sendBytes(packet) - self.lastTimeISentData = int(time.time()) - except: - print 'sendinv: self.sock.sendall failed' - break + if self.connectionIsOrWasFullyEstablished: # only send inv messages if we have send and heard a verack from the remote node + payload = '' + for hash in data: + if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: + payload += hash + if payload != '': + payload = encodeVarint(len(payload)/32) + payload + packet = shared.CreatePacket('inv', payload) + try: + self.sendBytes(packet) + except: + with shared.printLock: + print 'sendinv: self.sock.sendall failed' + break elif command == 'pong': self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): @@ -148,16 +143,16 @@ class sendDataThread(threading.Thread): packet = shared.CreatePacket('pong') try: self.sendBytes(packet) - self.lastTimeISentData = int(time.time()) except: - print 'send pong failed' + with shared.printLock: + print 'send pong failed' break elif command == 'sendRawData': try: self.sendBytes(data) - self.lastTimeISentData = int(time.time()) except: - print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.' + with shared.printLock: + print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.' break elif command == 'connectionIsOrWasFullyEstablished': self.connectionIsOrWasFullyEstablished = True @@ -172,5 +167,5 @@ class sendDataThread(threading.Thread): pass shared.sendDataQueues.remove(self.sendDataThreadQueue) with shared.printLock: - print 'Number of queues remaining in sendDataQueues:', len(shared.sendDataQueues) + print 'sendDataThread ending. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues) self.objectHashHolderInstance.close() diff --git a/src/shared.py b/src/shared.py index c82bf73a..cb3aa1c6 100644 --- a/src/shared.py +++ b/src/shared.py @@ -329,7 +329,7 @@ def isProofOfWorkSufficient( def doCleanShutdown(): global shutdown shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit. - broadcastToSendDataQueues((0, 'shutdown', 'all')) + broadcastToSendDataQueues((0, 'shutdown', 'no data')) with shared.objectProcessorQueueSizeLock: data = 'no data' shared.objectProcessorQueueSize += len(data) -- 2.45.1 From 0d2c94f0603f493479f7131bf8a7e62c7a22950e Mon Sep 17 00:00:00 2001 From: bmng-dev Date: Fri, 8 Aug 2014 01:34:57 +0000 Subject: [PATCH 05/12] Fix l10n so getTranslationLanguage always returns a string --- src/l10n.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/l10n.py b/src/l10n.py index c04b5cdb..cab012bd 100644 --- a/src/l10n.py +++ b/src/l10n.py @@ -8,15 +8,19 @@ import shared #logger = logging.getLogger(__name__) logger = logging.getLogger('file_only') + +DEFAULT_ENCODING = 'ISO8859-1' +DEFAULT_LANGUAGE = 'en_US' + +encoding = DEFAULT_ENCODING +language = DEFAULT_LANGUAGE + try: import locale - encoding = locale.getpreferredencoding(False) - language = locale.getlocale()[0] or locale.getdefaultlocale()[0] + encoding = locale.getpreferredencoding(False) or DEFAULT_ENCODING + language = locale.getlocale()[0] or locale.getdefaultlocale()[0] or DEFAULT_LANGUAGE except: logger.exception('Could not determine language or encoding') - if not encoding: - encoding = 'ISO8859-1' - language = 'en_US' time_format = shared.config.get('bitmessagesettings', 'timeformat') -- 2.45.1 From 0a2fb54f783d1a894b280e02c7da64c66cca084d Mon Sep 17 00:00:00 2001 From: bmng-dev Date: Fri, 8 Aug 2014 04:38:23 +0000 Subject: [PATCH 06/12] Fix l10n.formatTimestamp so it accepts strings --- src/l10n.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/l10n.py b/src/l10n.py index cab012bd..d097f2f9 100644 --- a/src/l10n.py +++ b/src/l10n.py @@ -11,6 +11,7 @@ logger = logging.getLogger('file_only') DEFAULT_ENCODING = 'ISO8859-1' DEFAULT_LANGUAGE = 'en_US' +DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' encoding = DEFAULT_ENCODING language = DEFAULT_LANGUAGE @@ -23,13 +24,38 @@ except: logger.exception('Could not determine language or encoding') -time_format = shared.config.get('bitmessagesettings', 'timeformat') +if shared.config.has_option('bitmessagesettings', 'timeformat'): + time_format = shared.config.get('bitmessagesettings', 'timeformat') + #Test the format string + try: + time.strftime(time_format) + except: + time_format = DEFAULT_TIME_FORMAT +else: + time_format = DEFAULT_TIME_FORMAT + def formatTimestamp(timestamp = None, as_unicode = True): - if timestamp and isinstance(timestamp, (float, int)): - timestring = time.strftime(time_format, time.localtime(timestamp)) - else: + #For some reason some timestamps are strings so we need to sanitize. + if timestamp is not None and not isinstance(timestamp, int): + try: + timestamp = int(timestamp) + except: + timestamp = None + + #timestamp can't be less than 0. + if timestamp is not None and timestamp < 0: + timestamp = None + + if timestamp is None: timestring = time.strftime(time_format) + else: + #In case timestamp is too far in the future + try: + timestring = time.strftime(time_format, time.localtime(timestamp)) + except ValueError: + timestring = time.strftime(time_format) + if as_unicode: return unicode(timestring, encoding) return timestring -- 2.45.1 From 33800064c7f19a5dafe37854743cfe5662c25086 Mon Sep 17 00:00:00 2001 From: bmng-dev Date: Sat, 16 Aug 2014 04:23:30 +0000 Subject: [PATCH 07/12] Update l10n to ensure decoding actually works Fix for encoding issue in #706 --- src/l10n.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/l10n.py b/src/l10n.py index d097f2f9..e3e8d07e 100644 --- a/src/l10n.py +++ b/src/l10n.py @@ -30,10 +30,31 @@ if shared.config.has_option('bitmessagesettings', 'timeformat'): try: time.strftime(time_format) except: + logger.exception('Could not format timestamp') time_format = DEFAULT_TIME_FORMAT else: time_format = DEFAULT_TIME_FORMAT +#It seems some systems lie about the encoding they use so we perform +#comprehensive decoding tests +if time_format != DEFAULT_TIME_FORMAT: + try: + #Check day names + for i in xrange(7): + unicode(time.strftime(time_format, (0, 0, 0, 0, 0, 0, i, 0, 0)), encoding) + #Check month names + for i in xrange(1, 13): + unicode(time.strftime(time_format, (0, i, 0, 0, 0, 0, 0, 0, 0)), encoding) + #Check AM/PM + unicode(time.strftime(time_format, (0, 0, 0, 11, 0, 0, 0, 0, 0)), encoding) + unicode(time.strftime(time_format, (0, 0, 0, 13, 0, 0, 0, 0, 0)), encoding) + #Check DST + unicode(time.strftime(time_format, (0, 0, 0, 0, 0, 0, 0, 0, 1)), encoding) + except: + logger.exception('Could not decode locale formatted timestamp') + time_format = DEFAULT_TIME_FORMAT + encoding = DEFAULT_ENCODING + def formatTimestamp(timestamp = None, as_unicode = True): #For some reason some timestamps are strings so we need to sanitize. -- 2.45.1 From 388bc6ba0466d412b78e93f682ad3f44bbd50e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20HERVIER?= Date: Thu, 28 Aug 2014 13:39:40 +0200 Subject: [PATCH 08/12] #708 : Use default locale encoding Use locale endoding else this generate error in GU : as default time format can have non ascii char on non english locale, but using ascii in locale can generate UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 : In PyBitmessage/src/bitmessageqt/__init__.py" l10n.formatTimestamp()) In PyBitmessage/src/l10n.py", line 81, in formatTimestamp return unicode(timestring, encoding) --- src/l10n.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/l10n.py b/src/l10n.py index e3e8d07e..f9c9b456 100644 --- a/src/l10n.py +++ b/src/l10n.py @@ -18,7 +18,7 @@ language = DEFAULT_LANGUAGE try: import locale - encoding = locale.getpreferredencoding(False) or DEFAULT_ENCODING + encoding = locale.getpreferredencoding(True) or DEFAULT_ENCODING language = locale.getlocale()[0] or locale.getdefaultlocale()[0] or DEFAULT_LANGUAGE except: logger.exception('Could not determine language or encoding') -- 2.45.1 From 6273ec6444577d756c91da2a9ab97fba3802d03a Mon Sep 17 00:00:00 2001 From: Michael Ford Date: Fri, 5 Sep 2014 11:48:59 +0800 Subject: [PATCH 09/12] Increment version number to 0.4.3 --- src/build_osx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_osx.py b/src/build_osx.py index f2bf8378..9652ca80 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -1,7 +1,7 @@ from setuptools import setup name = "Bitmessage" -version = "0.4.2" +version = "0.4.3" mainscript = ["bitmessagemain.py"] setup( -- 2.45.1 From 34824c044e4bd4b7774c0b8b78af6879270339b5 Mon Sep 17 00:00:00 2001 From: Yuri Date: Sun, 14 Sep 2014 23:34:33 -0700 Subject: [PATCH 10/12] 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 -- 2.45.1 From d3c91eea3bb960bcc85a1875d95fad3465265177 Mon Sep 17 00:00:00 2001 From: Yuri Date: Sun, 14 Sep 2014 23:53:21 -0700 Subject: [PATCH 11/12] 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 -- 2.45.1 From 5beaeff2e2dec032468f34464eb7a0b329efa41f Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 16 Sep 2014 10:04:56 -0700 Subject: [PATCH 12/12] 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 -- 2.45.1